closure
Closure:
JavaScript词缀作用域。javascript的函数在执行时是按照定义作用域链起作用。内嵌函数f()被包含在一个作用域链中,在这个作用域链中scope被赋值为"local scope"。无论f最终在哪里被执行,这种绑定一直存在。
var scope = "global scope"; function checkscope() { var scope = "local scope"; function f() { return scope; } return f; } console.log(checkscope()())
Define your own properties
函数是一种特殊的object类型,因此也可以有自己属性。
1 // Initialize the counter property of the function object 2 3 //Function decaralation are hoisted so we really can do this assignment before the function declaration 4 5 uniqueInteger.counter = 0; 6 7 //This function returns an unique integer each time when it is called. 8 9 //It uses a property of itself to remember the next value to be returned 10 11 function uniqueInteger() { 12 13 return uniqueInteger.counter++; 14 15 }
但是更安全的做法是使用闭包:
下面有个更像对象编程的:函数每一次被调用都产生了新的作用域链,并且其私有变量也是独立的。
1 function counter() { 2 3 var n = 0; 4 5 return {count:function () { 6 7 return n++; 8 9 }, reset:function () { 10 11 n = 0; 12 13 }}; 14 15 } 16 17 var c = counter(), d = counter(); 18 19 console.log("c.count(): " + c.count()); //0 20 21 console.log("d.count(): " + d.count()); //0 c 和 d的count是独立的 22 23 console.log("c.reset(): " + c.reset()); //reset()和count()共享状态 24 25 console.log("c.count(): " + c.count()); //0 因为刚把creset()了 26 27 console.log("d.count(): " + d.count()); //1 d不受影响
注意:以上我们见识了在同一个作用域链定义的闭包,会分享外层函数的私有成员。这是一个非常重要的技术,但是有时候闭包不应该共享变量如下:内嵌函数并拷贝作用域或者保留变量的静态快照。对于内嵌函数而言,i是共享的,并且是动态的。
1 // This function returns a function that always returns v [这个不是在for循环内定义闭包,而是提前定义,在循环内被调用,每次调用就产生作用域链] 2 3 function constfunc(v) { return function() { return v; }; } 4 5 // Create an array of constant functions: 6 7 var funcs = []; 8 9 for(var i = 0; i < 10; i++) funcs[i] = constfunc(i); 10 11 // The function at array element 5 returns the value 5. 12 13 funcs[5]() // => 5
并不同于如下
1 // Return an array of functions that return the values 0-9 2 3 function constfuncs() { 4 5 var funcs = []; 6 7 for(var i = 0; i < 10; i++) 8 9 funcs[i] = function() { return i; }; //[在for循环内定义了10个闭包,且都只是在一次constfuncs函数的一次调用中完成,而他们都引用i,因此他们分享同一个i的状态。] 10 11 return funcs; 12 13 } 14 15 var funcs = constfuncs(); 16 17 console.log(funcs[5]()) // What does this return? 返回10,且所有的都返回10
多重闭包:上面的问题我们可以通过多重闭包来解决
1 /* 使用匿名函数来激发出 创建多重闭包函数所需要的 作用域 */ 2 3 function constfuncs() { 4 5 var funcs = []; 6 7 for(var i = 0; i < 10; i++) { 8 9 //! funcs[i] = function() { return i; }; 10 11 //使用自执行的匿名函数来激发出作用域 12 13 (function(){ 14 15 var v=i; // 记住在这个作用域内的值,这句必须有,值的复制 16 17 funcs[i]=function(){ //这个下标既可用i也可以用v,因为这个声明语句是被执行了的 18 19 console.log(i+" "+v); // i都等于10 20 21 return v; //在这个内层函数里,必须使用外层无法改变其值的本地变量v,如果使用i那么以在本函数执行时的i值为准; 22 23 } 24 25 //!funcs[i]=function(){return i;} //回调函数内不可使用i,如果使用i,返回的匿名的i值随着循环的变化。实际上使用了外层可改变值的i 26 27 }()); 28 29 } 30 31 return funcs; 32 33 }
中间的那一部分也可以写成如下,这样就更容易理解了:
1 funcs[i] = (function () { 2 3 var v = i; // 记住在这个作用域内的值,这句必须有,值的复制 4 5 return function () { //这个下标既可用i也可以用v 6 7 console.log(i + " " + v); // i都等于10 8 9 return v; //在这个内层函数里,必须使用本地变量v 10 11 } 12 13 //!funcs[i]=function(){return i;} //回调函数内不可使用i 14 15 }()); 16 17
或者:
funcs[i] = (function (v) { //通过匿名函数传入i值,省去显示声明v,这个v是匿名函数的本地变量,外层函数无访问权限,无法改变
return function () { //这个下标既可用i也可以用v
return v; //在这个内层函数里,必须使用v;
}
}(i));
或者还可以写作:
1 funcs[i] = new function () { 2 3 var v = i; // 记住在这个作用域内的值,这句必须有,值的复制 4 5 return function () { //这个下标既可用i也可以用v 6 7 console.log(i + " " + v); // i都等于10 8 9 return v; //在这个内层函数里,必须使用v; 10 11 } 12 13 //!funcs[i]=function(){return i;} //回调函数内不可使用i 14 15 }();
注意:内嵌函数不能直接访问外部函数的this,arguments。如果需要,必须在外部函数范围定义一个变量来保证内嵌函数能正确访问。
1 var self = this; // Save this value in a variable for use by nested funcs. 2 3 var outerArguments = arguments; // Save for use by nested functions