您的位置:首页 > Web前端 > JavaScript

【JavaScript高级程序设计】读书摘录4-第七章 匿名函数

2015-04-10 23:52 507 查看

第七章、匿名函数

本章中比较重要的几个概念:闭包匿名函数作用域链this对象等。在js中碰到的很多坑都来自本章。另外,在很多类库中如jQuery中都会大量使用闭包和匿名函数,它们都是非常有用的特性。

1、函数定义和函数表达式的区别:对于函数定义,在代码执行以前会被加载到内存中,而对于函数表达式:只有在代码执行到那一行的时候才有定义:

function func(){

}

var func = function(){

}

2、可以用arguments.callee实现递归,比直接使用函数名更加保险。即使你的函数更改为另外的名字,使用arguments.callee可以保证递归正确执行。

3、闭包和匿名函数的区别:

闭包是有权访问另一个函数作用域中的变量的函数。创建闭包的最常见方式,就是在一个函数内部创建另外一个函数。理解如何创建作用域链,以及作用域链的细节,对于理解闭包非常重要。作用域链本质是一个指向变量对象的指针列表,它只引用而不实际包含对象。

作用域链:当函数第一次被调用时,会创建一个执行环境以及相应的作用域链,并把作用域链复制给一个特殊的内部属性[[Scope]],然后,利用this,arguments和其他命名参数的值来初始化函数的活动对象。

后台的每个一执行环境都有一个表示变量的对象-变量对象。全局环境的变量对象始终存在。而对于函数的局部环境的变量对象,只在函数执行的过程中存在。

在另一个函数中定义的函数会将包含函数(外部函数)的活动对象添加到它的作用域链中。

function createCompare( proper ){
         returnfunction( obj1, obj2) {
                   varval1 = obj1[proper];
                   varval2 = obj2[proper];               
                   returnval1 - val2;
         }
}

var obj = [
         {id: 10, age : 20},
         {id: 12, age : 16},    
         {id: 15, age : 22},    
];
var compare = createCompare('age');

obj.sort(compare);
console.log(obj);

在匿名函数从createCompare函数返回之后,它的作用域链被初始化为包含createCompare函数的活动对象和全局变量对象。这样匿名函数就可以访问在createCompare函数中定义的所有变量。更为重要的是,createCompare函数在执行完毕之后,它的活动对象也不会销毁,因为匿名函数的作用域链依然在引用这个活动对象。换句话说:在createCompare函数返回后,其执行环境的作用域链会被销毁,但是它的活动对象依然会保留在内存中,直到匿名函数被销毁后,createCompare的活动对象才会被销毁。

compare = null;

由于闭包会携带包含它的函数的作用域,因此会比其他的函数占用更多的内存。

闭包与变量

由于闭包的特性,导致闭包只能取得包含函数中任意变量的最后一个值。闭包中保存的是整个变量对象,而不是某个特殊的变量:

function create(){
   var result = [];
   for( var i = 0; i < 10; i++ ){
       result[i] =  function(){
           return i;
       };
    }
   return result;

}

由于闭包引用的外部函数的变量i,因此每个函数都保存着同一个变量对象,所以在每个函数内部i的值都是10.可以通过创建另一个匿名函数强制让闭包的行为符合预期:

function create(){
   var result = [];
   for( var i = 0; i < 10; i++ ){
       result[i] =  function(num){
           return function(){
                  return num;
            }
       }(i);
    }
   return result;

}

关于this对象:

this对象是在运行时给予函数的执行环境绑定的,在全局函数中,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。不过匿名函数的执行具有全局性,因此其this对象通常指向window.在通过call()或者apply()改变函数执行环境的情况下,this指向其他对象。

var name = "the window";

var obj = {
    name : "the obj",

    getFunc: function(){
        return function(){
            return this.name;
        }
    }
}

console.log( obj.getFunc()() );

可以把外部作用域的this变量包含在一个闭包能够访问的变量里,就可以让闭包访问该对象了:

var name = "the window";

var obj = {
    name : "the obj",

    getFunc: function(){
		var that = this;
        return function(){
            return that.name;
        }
    }
}

console.log( obj.getFunc()() );


this和arguments也存在同样的问题。如果想要访问作用域中的arguments对象,必须将对该对象引用保存到另一个闭包能够访问的变量中。

闭包与内存泄露:

由于IE对于JScript对象和COM对象使用不同的垃圾收集机制(IE对Javascript采用标记清除的垃圾收集机制,而DOM和BOM对象并非原生的Javascript对象,是通过引用计数机制来进行垃圾收集的),因此闭包在IE中会导致一些特殊的问题。如果闭包的作用域链中包含了一个HTML元素,那么就意味着该元素无法被销毁:

function setHandler(){
     var elem = document.getElementById("someElement");
     elem.onclick= function(){
          console.log(elem.id );
     }
}

以上代码创建了一个elem元素的时间处理程序的闭包,而这个闭包又创建了一个循环引用。由于匿名函数保存了一个对assignHandler的活动对象引用,因此在assignHandler执行完毕退出时,并不会销毁assignHandler的活动对象。因此只要有匿名函数存在,elem的引用数至少也是1. 解决方法:

functionsetHandler(){
    var elem =document.getElementById("someElement");
    var id = elem.id;
    elem.onclick = function(){
        console.log( id );
    }
    elem = null;

}

块级作用域:javascript中没有块级作用域的概念,要模拟块级作用域,可以用下面的语法:

(function(){

})();

也就是定义一个匿名函数并立即调用它。

这个技术最常在全局作用域中被用于函数外部,从而限制向全局作用域中添加过多的变量和函数。这种方法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。

私有变量:

function MyObject(){

        var privateVar = 10;
        
        function privateFunc(){
                console.log(privateVar );
        }

        this.publicFunc= function(){
               privateVar++;
               privateFunc();
        }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: