JavaScript中的函数:闭包,this,高阶函数

一.函数基本理论

function compare(val1,val2){
    return val1 - val2; }
var result = compare(5,10);

 

1,函数的定义没什么意义,以后建立一个字符串,就是函数代码数组

2,函数执行(被调用)的时候发生的事情:(以上面的代码为例)浏览器

建立一个执行环境execution context ,该对象有一个特殊的属性叫[scope chain] 做用域链,属性的值是一个类数组对象,如上图所示,第一个包含了arguments,val1和val2的活动对象,第二个是包含了compare和result,的活动对象。注意,这里的活动对象都没有加进去this关键字,这是由于,当函数内部经过做用域链寻找一个变量时候,与this关键字没有任何关系,因此不会从做用域链上面解析。闭包

理解函数的基本原理对于函数的理解函数闭包的概念颇有帮助。app

二.高阶函数

1.函数做为参数传递dom

最经典的例子就是毁掉函数函数

var fs  = require('fs');
fs.readFile('test.txt',function(data,err){ console.log(data); });

2.函数做为返回值性能

做为返回值时候,要注意此时的this指向。ui

3.函数柯里化this

函数柯里化指首先接受一些参数,接受到的参数后不当即执行,而是返回一个新函数,刚才传入的参数在函数造成的闭包中被保存起来,待到真正求值的时候刚才保存的参数才会真正的求值。spa

var cost = (function(){
    var args = []; return function(){ if(arguments.length===0){ var money =0; for(var i-0;i<args.length;i++){ money+=args[i]; } return money; }else{ [].push.apply(args,arguments); } } })(); cost(100);//100 cost(200);//200 cost();//300

 

4.函数节流

函数节流的思想就是让一些频繁执行的函数减小执行频率;好比由于浏览器窗口变化引发resize事件的频繁执行,mouseover,上传进度等等。

var throttle = function(fn,interval){
    var _self = fn,timer,firstTime; return function(){ var args = arguments,_me = this; if(firstTime){ _self.apply(_me,args); return firstTime = false; } if(timer){ return false; } timer = setTimeout(function(){ clearTimeout(timer); timer = null; _self.apply(_me,args); },interval||500); } }; window.onresize = throttle(function(){ console.log(1)},500);

代码的解决办法是利用定时器延迟执行,若是定时器在规定时间后还没执行完,那么,下一次执行的时候就不会执行,直接返回;

 5.分时函数

分时函数应用的场景好比,你的QQ好友有上千个,每个好友是一个dom,这是加载的时候浏览器可能吃不消,就要用到setInterval函数来延迟加载。

//ary须要加载的数据,fn加载逻辑,count每一批加载的个数
var timeChunk = function(ary,fn,count){ var obj, t; var len = ary.length; var start = function(){ for(var i=0;i<Math.min(count||1,ary.length);i++){ var obj = ary.shift(); fn(obj); } }; return function(){ t = setInterval(function(){ if(ary.length===0){ return clearInterval(t); } start(); },200); } }
var ary = [];
for(var i=0;i<1000;i++){
ary.push(i);
}
var renderFirendList = timeChunk(ary,function(n){
var div = document.createElement('div');
div.innerHTML = n;
document.body.appendChild(div);
},8);
renderFirendList();

 6.惰性加载函数

惰性 加载函数也很常见,好比浏览器嗅探中的时间绑定函数

var addEvent = function(elem,type,handler){
    if(window.addEventListener){ return elem.addEventListener(type,handler,false); } if(window.addEvent){ return elem.addEvent('on'+type,handler); } }

 

 以上代码在非IE浏览器下每次都不会走第二个分支,而且每次添加一个事件就会执行一次判断,虽然这不会增长性能开销,可是能够利用惰性加载来解决

var addEvent = function(elem, type, handler){
    if(window.addEventListener){ addEvent = function(elem, type, handler){ elem.addEventListener(type, handler, false) } }else if(window.addEvent){ addEvent = function(elem, type, handler){ elem.addEvent('on'+type, handler); } } addEvent(elem,type,handler); }

 三.this

this的判断只要记住一点,就是在执行的时候动态绑定的,而不是函数声明时候绑定,如下是优先级

if(hava new){

  this 就是new返回的这个对象

}else if(hava call,apply 绑定){

  apply,call绑定的那个对象就是this

}else if(有对象调用){

  this就是这个调用的对象

}else{

  默认绑定到window //这种状况通常是闭包

}

window.name = 'globalname';
var obj = {}; obj.name = 'lucy'; obj.show = (function(){ console.log(this.name); return function(){ console.log(this.name)} })() obj.show(); VM928:6 globalname VM928:7 lucy

对于上面的代码,obj.show定义为一个对象的方法,可是该方法当即执行,而且是在全局的做用域下执行的,因此输出为globalname,当obj.show执行完以后返回了一个函数赋值给obj.show,说以obj.show此时才真正是对象的方法,因此第二个返回lucy,这个例子完美的证实了运行时动态绑定this。

另外,有一点就是对于事件绑定函数,this指向那个绑定的元素,settimeout中的延迟函数中的this指向window。

this在递归调用中是个坑!!!!!!!!!!!!!!注意注意

四.闭包

闭包是有权访问另一个函数做用域中的变量的函数。《JavaScript高级程序设计第三版》。

典型的例子是一个内部函数访问外部函数的变量,即便这个内部函数返回了或者是被调用了,仍然能够访问外部变量,以下

function com(propertyName){
    return function(obj1,obj2){ var value1 = obj1[propertyName]; var value2 = obj2[propertyName]; return value1 - value2; } } var obj1 ={'name':1}; var obj2 ={'name':2}; var compare = com('name'); console.log(compare(obj1,obj2));

上面例子中,匿名函数访问了外部函数的局部变量propertyName,而且当它返回了,并且是在其余地方被调用了仍然能够访问。好比com函数返回了一个匿名函数,而且在其余地方被调用了这个函数,可是仍然能够访问propertyName变量对象,可是有一点就是,这里的propertyName是一个变量对象(活动对象)而不是变量自己,若是是在for等循环语句中就会出现错误。以下面的例子:

function foo(){

    var result = []; for(var i = 0; i < 5; i++){ result[i] = function(){ return i; } } return result; } var s = foo(); console.log(s[1]());//5

 

上面代码输出结果是5的缘由是每个内部匿名函数包含的活动对象是i这个变量对象,因此最终foo()执行返回的每个result[i](这里的i没有变成5是由于它没在匿名函数内),都是外部foo活动对象,因此最终结果就是5.避免这种结果的方法就是在外面继续增长一层做用域,使每个result[i]函数都持有本身i的活动对象。

function bar(){
    var result = []; for(var j = 0; j < 5; j++){ result[j] = (function(num){ return function(){ return num; } })(j) } return result; }

 

这2段代码的函数活动对象图以下:

第一个代码全部的result都引用函数foo中活动对象i因此当foo执行完返回后,i变量的值是5因此出现如上所示。

第二段代码中,因为参数是按值复制传递的,因此j会一次赋值给num,最内层的匿名函数保存了3个活动对象,分别是当即执行函数,bar,和window,而且当即执行函数也是有5个,而且保存了5个num值,这样就能够达到预期的效果。

闭包在实际开发中的一些应用:

1.setTimeout函数中的应用

for (var i = 0; i < 3; i++) {
            setTimeout(function() {
                console.log(i);
            }, 0)
        }
        //4
        //4
        //4

 

这个运行结果的解释就是i变量只有一个,可是有3个匿名函数都引用了。

2.事件绑定与闭包

for( var i=0; i<5; i++ ) {
   document.getElementById("p"+i).onclick=function() {
     alert(i); //访问了父函数的变量i, 闭包,结果都弹出5
   };
 };

 

若是用户点击每个按钮,它弹出的都是5,由于当用户点击的时候这里的i值都变成了5,有点相似于运行时动态绑定的概念,也相似于C语言中的指针,由于i变量是一个虽然存在于外部window对象上,可是内部函数引用了这个变量,因此它不管函数返回都常驻内存,alert的时候就访问了这个内存中的区域。

 

 

 

 

 

参考文献:

1.《JavaScript高级程序设计》

2.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Method_binding

相关文章
相关标签/搜索