闭包的概念及应用。
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数.
这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置0的函数返回 0,位置 1 的函数返回1,以此类推。
但实际上,每个函数都返回10。因为每个函数的作用域链中都保存着 createFunctions() 函数的活动对象 , 所以它们引用的都是同一个变量i。 当createFunctions()函数返回后,变量i的值是10,此时每个函数都引用着保存变量 i的同一个变量对象,所以在每个函数内部 i 的值都是 10。
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { // 这里只是把匿名函数push进数组 并没有真正执行 // 因为这里只是一个声明 并没有调用 result[i] = function () { return i; }; } return result;}var funcs = createFunctions();for (var i = 0; i < funcs.length; i++) { // 调用这个函数的时候才执行 在那个作用域中i的值为10 console.info(funcs[i]());}复制代码
修改通过创建另一个匿名函数强制让闭包的行为符合预期
for (var i = 0; i < 10; i++) { result[i] = function (num) { return function () { return num; }; }(i);}复制代码
创建块级作用域
用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。
(function(){ //这里是块级作用域})();复制代码
以上代码定义并立即调用了一个匿名函数。 将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。 如果有读者感觉这种语法不太好理解,可以再看看下面这个例子。
var count = 5;outputNumbers(count);复制代码
这里初始化了变量 count,将其值设置为5。当然,这里的变量是没有必要的,因为可以把值直接传给函数。为了让代码更简洁,我们在调用函数时用 5 来代替变量 count,如下所示。
outputNumbers(5);复制代码
这样做之所以可行,是因为变量只不过是值的另一种表现形式,因此用实际的值替换变量没有问题。 再看下面的例子。
var someFunction = function(){ //这里是块级作用域};someFunction();复制代码
这个例子先定义了一个函数,然后立即调用了它。定义函数的方式是创建一个匿名函数,并把匿名函 数 赋 值 给 变 量 someFunction 。 而 调 用 函 数 的 方 式 是 在 函 数 名 称 后 面 添 加 一 对 圆 括 号 , 即someFunction()。通过前面的例子我们知道,可以使用实际的值来取代变量 count,那在这里是不是也可以用函数的值直接取代函数名呢? 然而,下面的代码却会导致错误。
function(){//这里是块级作用域}(); //出错!复制代码
这段代码会导致语法错误,是因为 JavaScript 将 function 关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。要将函数声明转换成函数表达式, 只要像下面这样给它加上一对圆括号即可。
(function(){//这里是块级作用域})();复制代码
使用块级作用域后的改进
for (var i = 0; i < 10; i++) { // 构造块级作用域 将i传给index // 这里会立即执行 (function (index)) 因为这就是一个调用函数的表达式 // 从而创建了块级作用域保存了当前的i // 但是不会立即执行 result[i] = function() // 以为这里只是声明 并没有调用 (function (index) { result[i] = function () { return index; }; })(i);}复制代码