日期:2014-05-16 浏览次数:20460 次
五、闭包的微观世界
继续使用上篇的代码:
?
function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(); c();
?如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。
?
?
?
到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。
?
当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:
?
如图所示,当在函数b中访问一个变量的时候,搜索顺序是:
?
小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:
?
function f(x) { var g = function () { return x; } return g; } var h = f(1); alert(h());
这段代码中变量h指向了f中的那个匿名函数(由g返回)。
?
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。
运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。?
?
六、闭包的应用场景
?
?
?
function Constructor(...) { var that = this; var membername = value; function membername(...) {...} }
?以上3点是闭包最基本的应用场景,很多经典案例都源于此。
?
七、Javascript的垃圾回收机制
?
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
?
八、使用闭包的注意点
?
九、几个有用的示例
?
//*************闭包uniqueID************* uniqueID = (function(){ //这个函数的调用对象保存值 var id = 0; //这是私有恒久的那个值 //外层函数返回一个有权访问恒久值的嵌套的函数 //那就是我们保存在变量uniqueID里的嵌套函数. return function(){return id++;}; //返回,自加. })(); //在定义后调用外层函数. document.writeln(uniqueID()); //0 document.writeln(uniqueID()); //1 document.writeln(uniqueID()); //2 document.writeln(uniqueID()); //3 document.writeln(uniqueID()); //4
?
?
//*************闭包阶乘************* var a = (function(n){ if(n<1){ alert("invalid arguments"); return 0; } if(n==1){ return 1; } else{ return n * arguments.callee(n-1); } })(4); document.writeln(a);
?