Javascript学习笔记之 函数篇(三) : 闭包和引用


Posted in Javascript onNovember 23, 2014

Javascript 中一个最重要的特性就是闭包的使用。因为闭包的使用,当前作用域总可以访问外部的作用域。因为 Javascript 没有块级作用域,只有函数作用域,所以闭包的使用与函数是紧密相关的。

模拟私有变量

function Counter(start) {

    var count = start;

    return {

        increment: function() {

            count++;

        },

        get: function() {

            return count;

        }

    }

}

var foo = Counter(4);

foo.increment();

foo.get(); // 5

这里 Counter 返回两个闭包:函数 increment 和 get。这两个函数一直保持着对 Counter 作用域的访问,因此它们能一直访问到定义在 Counter 作用域的变量 count。

私有变量的工作机制

由于 Javascript 不可以对作用域赋值和引用,所以在上例中,是没有办法在外部直接访问内部私有变量 count。唯一的方法就是通过定义闭包来访问。

var foo = new Counter(4);

foo.hack = function() {

    count = 1337;

};

上面的代码不会改变 Counter 作用域内的 count 变量值,因为 hack 没有在 Counter 内定义。上面这段代码只会创建或者覆盖全局变量 count。

循环内的闭包

一个最容易犯的错误就是在循环内使用闭包。

for(var i = 0; i < 10; i++) {

    setTimeout(function() {

        console.log(i);  

    }, 1000);

}

上面这段代码不会输出0到9,而是连续输出10次10。
上面的匿名会一直保持一个对变量 i 的引用。当调用 console.log 函数开始输出时,这是循环已经结束,而变量 i 已经为10了。
为了避免上面的错误发生,我们需要在每次循环时为变量 i 值创建一个拷贝。

避免引用错误

为了复制循环中变量的值,最好的方式是在外层加一个匿名的立刻执行函数。

for(var i = 0; i < 10; i++) {

    (function(e) {

        setTimeout(function() {

            console.log(e);  

        }, 1000);

    })(i);

}

这个外部的匿名函数接收循环变量 i 作为第一个参数,并将其值拷贝至它自身的参数 e。
外部的匿名函数将参数 e 再传递给 setTimeout,因此 setTimeout 有了指向参数 e 的引用。而且这个参数 e 的值不会因为外部的循环改变而改变。

还有另外一个方法可以实现同样的效果,就是在 setTimeout 内的匿名函数中再返回一个匿名函数:

for(var i = 0; i < 10; i++) {

    setTimeout((function(e) {

        return function() {

            console.log(e);

        }

    })(i), 1000)

}

此外,通过 bind 方法也可以实现。

for(var i = 0; i < 10; i++) {

    setTimeout(console.log.bind(console, i), 1000);

}

文章最后我们来总结下:

(1)闭包是一种设计原则,它通过分析上下文,来简化用户的调用,让用户在不知晓的情况下,达到他的目的;
(2)网上主流的对闭包剖析的文章实际上是和闭包原则反向而驰的,如果需要知道闭包细节才能用好的话,这个闭包是设计失败的;
(3)尽量少学习。

Javascript 相关文章推荐
B/S开发中常用javaScript技术与代码
Mar 09 Javascript
jQuery 页面载入进度条实现代码
Feb 08 Javascript
jquery三个关闭弹出层的小示例
Nov 05 Javascript
JavaScript节点及列表操作实例小结
Aug 05 Javascript
用AngularJS来实现监察表单按钮的禁用效果
Nov 02 Javascript
利用angular.copy取消变量的双向绑定与解析
Nov 25 Javascript
JavaScript中双符号的运算详解
Mar 12 Javascript
基于vue2实现左滑删除功能
Nov 28 Javascript
JS使用正则表达式获取小括号、中括号及花括号内容的方法示例
Jun 01 Javascript
JavaScript对象拷贝与赋值操作实例分析
Dec 10 Javascript
AntV F2和vue-cli构建移动端可视化视图过程详解
Oct 08 Javascript
js实现有趣的倒计时效果
Jan 19 Javascript
js实例属性和原型属性示例详解
Nov 23 #Javascript
JS常用函数使用指南
Nov 23 #Javascript
浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
Nov 23 #Javascript
理解jQuery stop()方法
Nov 21 #Javascript
JS中三目运算符和if else的区别分析与示例
Nov 21 #Javascript
node.js使用npm 安装插件时提示install Error: ENOENT报错的解决方法
Nov 20 #Javascript
JS在可编辑的div中的光标位置插入内容的方法
Nov 20 #Javascript
You might like
php 常用字符串函数总结
2008/03/15 PHP
PHP自动生成后台导航网址的最佳方法
2013/08/27 PHP
PHP下的Oracle客户端扩展(OCI8)安装教程
2014/09/10 PHP
PHP清除数组中所有字符串两端空格的方法
2014/10/20 PHP
php+mysql查询实现无限下级分类树输出示例
2016/10/03 PHP
Laravel框架使用monolog_mysql实现将系统日志信息保存到mysql数据库的方法
2018/08/16 PHP
12306验证码破解思路分享
2015/03/25 Javascript
JavaScript 数据类型详解
2017/03/13 Javascript
ES6新特性七:数组的扩充详解
2017/04/21 Javascript
Js实现中国公民身份证号码有效性验证实例代码
2017/05/03 Javascript
React组件refs的使用详解
2018/02/09 Javascript
vue实现tab切换外加样式切换方法
2018/03/16 Javascript
使用vue-router完成简单导航功能【推荐】
2018/06/28 Javascript
如何自定义微信小程序tabbar上边框的颜色
2019/07/09 Javascript
jQuery HTML设置内容和属性操作实例分析
2020/05/20 jQuery
[31:47]夜魇凡尔赛茶话会 第三期01:选手知多少
2021/03/11 DOTA
python 多进程通信模块的简单实现
2014/02/20 Python
Python装饰器基础详解
2016/03/09 Python
Python 绘图和可视化详细介绍
2017/02/11 Python
基于Python闭包及其作用域详解
2017/08/28 Python
python的内存管理和垃圾回收机制详解
2019/05/18 Python
pyinstaller参数介绍以及总结详解
2019/07/12 Python
python3 map函数和filter函数详解
2019/08/26 Python
在Python中画图(基于Jupyter notebook的魔法函数)
2019/10/28 Python
python实现图像拼接
2020/03/05 Python
Python实现Canny及Hough算法代码实例解析
2020/08/06 Python
澳大利亚百货商店中销量第一的商务衬衫品牌:Van Heusen
2018/07/26 全球购物
意大利单身交友网站:Meetic
2020/07/12 全球购物
教师师德反思材料
2014/02/15 职场文书
大学开学计划书
2014/04/30 职场文书
党员教师一句话承诺
2014/05/30 职场文书
实验室标语
2014/06/21 职场文书
学校安全管理责任书
2014/07/23 职场文书
班主任师德师风自我剖析材料
2014/10/02 职场文书
红白喜事主持词
2015/07/06 职场文书
新郎父母婚礼致辞
2015/07/27 职场文书