闭包的深刻理解,解释很详细
2016-03-10 15:12
441 查看
闭包虽不是JS的特色功能, 但要理解还真要费那么点工夫. 在理解闭包之前, 首先要清楚JS中的作用域只有2种: 全局作用域和方法作用域 全局作用域很好理解了, 方法作用域就是指一个 function 形成一个独立的作用域, 而且方法作用域还能够嵌套. 与别的语言不同的是: 花括号({})不能形成一个独立的作用域, 例如Java中的作用域. 下面我们举例说说作用域 var g = 0; function f() { // 这里面就形成了一个方法作用域, 能够保护其中的变量不能被外部访问 // 方法作用域能够访问全局作用域 var a = 1; console.log(g); // 嵌套方法作用域 function ff() { // 这里面再度形成了一个方法作用域 // 其中可以访问外部的那个方法作用域 var aa = 2; console.log(a); } // 出了 ff 的作用域就不能访问其中的东西了 // console.log(aa); // 报错 ReferenceError: aa is not defined } f(); // console.log(a); // 报错 ReferenceError: a is not defined 弄清楚作用域的问题后, 我们就可以开始聊聊闭包了. 我们以最经典的for循环为例来讲解. 大家可以试试下面这段代码, 取自JavaScript 秘密花园循环中的闭包 for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); } 如果在运行前, 你没有猜到正确答案, 那就对了... 1. 首先说说为什么最终输出的是10次10, 而不是你想象中的 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 因为setTimeout是异步的! 你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分 第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout 因此我们可以得到如下代码 // 第一个部分 i++; ... i++; // 总共做10次 // 第二个部分 setTimeout(function() { console.log(i); }, 1000); ... setTimeout(function() { console.log(i); }, 1000); // 总共做10次 这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了. 由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了! i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成. for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 0); } 2. 那么为什么setTimeout中匿名function没有形成闭包呢? 因为setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function. 因此如果我们定义一个外部函数, 让 i 作为参数传入即可"闭包"我们要的变量了!! for (var i = 0; i < 10; i++) { // 注意关键是我们把想要闭包的值当参数传入一个方法 // 这个方法 return 一个新的方法 -- 闭包!! setTimeout(fn(i), 1000); } function fn() { // 为了深刻理解闭包, 这个函数我没有用参数 // 神奇的"闭包"发生在这一步, 其实就是作用域和值复制在起了关键作用, // 对于数字/字符等类型是复制值, 而不是引用 var a = arguments[0]; return function() { console.log(a); // 注意现在我操作的变量已经变成 a 了, // 已经和 i 没有半毛线关系了! // 而 a 的值就是当时执行时赋予的一个确定值, // 不会因 i 的变化而变化了! }; } 3. 再换成更简洁的方式看你能不能真正理解闭包 for (var i = 0; i < 10; i++) { (function(a) { // 变量 i 的值在传递到这个作用域时被复制给了 a, // 因此这个值就不会随外部变量而变化了 setTimeout(function() { console.log(a); }, 1000); })(i); // 我们在这里传入参数来"闭包"变量 } //这就是我所理解的闭包, 简单点说就是专门用来"包养"变量的.
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- 深入理解PHP之匿名函数
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子