Javascript之闭包(翻译)
2018.01.31
chen7
js相关
 热度
℃
原文地址:http://javascriptissexy.com/understand-javascript-closures-with-ease/#
使用闭包可以让我们写出更具有创造性,能简明清晰表达的代码。事实上我们都在经常使用闭包,不管你使用javascript的经验有多少,你都会不自觉的一次又一次用到它。当然有时候闭包可能会显得有些复杂超出了你的理解范围,但是读了这篇文章之后,你一定会更轻松容易的理解它,另外也会更吸引你在每天的js变成任务中使用它。
这是一篇相当短小的文章详细的介绍了javascrip的闭包,阅读之前你需要弄清楚javascript作用域,这可以帮助你更好的理解。
什么是闭包?
一个闭包是一个可以访问外部函数变量作用域的内部函数。
闭包可以有三个作用域链:定义在他自身函数内部的变量,他也可以使用父级函数的变量,还可以使用全局变量。
内部的函数不仅能使用外部函数的变量,还可以使用外部函数的参数,但是不能使用外部函数的arguments对象。
你可以很容易创建一个闭包,通过把一个函数添加到另一个函数内部就可以了。
javascript中的一个常见例子
1 2 3 4 5 6 7 8 9 10
| function showName (firstName, lastName) { var nameIntro = "Your name is "; function makeFullName () { return nameIntro + firstName + " " + lastName; } return makeFullName (); }
showName ("Michael", "Jackson");
|
在Node.js中经常使用到闭包,在jquery或者很多javascript片段中也经常使用到闭包的概念。
jQuer中一个常见的例子
1 2 3 4 5 6
| $(function() { var selections = []; $(".niners").click(function() { selections.push (this.prop("name")); }); });
|
闭包的使用规则和一些副作用
1.闭包可以使用外部函数的变量哪怕外部函数有返回值:
关于闭包最重要的一个功能就是内部函数可以使用外部函数的变量,哪怕外部函数有了返回值。是的,这句话你没听错,当函数在javascript中执行时,他们使用相同的作用域链当他们一旦被创建,这意味着即便外层函数有了新的返回值,内部函数依然可以访问了外部函数的变量,看下面这个例子演示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function celebrityName (firstName) { var nameIntro = "This celebrity is "; function lastName (theLastName) { return nameIntro + firstName + " " + theLastName; } return lastName; } var mjName = celebrityName ("Michael");
mjName ("Jackson");
|
2.闭包的储存值依据于外层函数的变量
他们并不会储存实际被返回的值,在闭包被执行之前它对外层函数的变量改变更感兴趣。这个特点让闭包在某些代码的处理方式上更具有积极性,比如一下这个私有变量的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function celebrityID () { var celebrityID = 999; return { getID: function () { return celebrityID; }, setID: function (theNewID) { celebrityID = theNewID; } }
}
var mjID = celebrityID ();
mjID.getID(); mjID.setID(567); mjID.getID();
|
3.闭包所引发的问题
因为闭包可以改变外层函数的变量,所以他们同样可以引起很多bug当外层函数执行for循环的时候,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function () { return uniqueID + i; } } return theCelebrities; }
var actionCelebs = [ {name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs[0]; console.log(stalloneID.id());
|
前面这个函数,当匿名函数被执行时,i的值是3(即数组的长度),这个数字3被加到了uniqueID中即103,所以任何位置都可以得到数组的id=103,而不是类似的100,101,102。
发生这个情况的原因是,当我们在讨论前面这个案例的时候,这个闭包(即例子中的匿名函数)已经获取了外层函数的变量作为参考,而不是他的返回值,所以就像前面案例显示的那样我们可以利用闭包使用最新的变量值,这个例子中同样可以使用改变之后的i变量,因为外层函数执行了整个循环返回了最后的i值103.
为了弥补这个闭包所引发的负影响,你可以用立即执行函数(IIFE)像下面这样去使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function (j) { return function () { return uniqueID + j; } () } (i); } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id);
var cruiseID = createIdForActionCelebs [1]; console.log(cruiseID.id);
|