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 相关文章推荐
JQuery 学习笔记 选择器之五
Jul 23 Javascript
js中方法重载如何实现?以及函数的参数问题
Aug 01 Javascript
调用DOM对象的focus使文本框获得焦点
Feb 19 Javascript
JavaScript简单遍历DOM对象所有属性的实现方法
Oct 21 Javascript
微信小程序 获取当前地理位置和经纬度实例代码
Dec 05 Javascript
Javascript oop设计模式 面向对象编程简单实例介绍
Dec 13 Javascript
详解Python中logging日志模块在多进程环境下的使用
Dec 26 Javascript
XMLHttpRequest对象_Ajax异步请求重点(推荐)
Sep 28 Javascript
JS处理数据四舍五入(tofixed与round的区别详解)
Oct 26 Javascript
Node.js 如何利用异步提升任务处理速度
Jan 07 Javascript
Vue axios 将传递的json数据转为form data的例子
Oct 29 Javascript
如何理解Vue简单状态管理之store模式
May 15 Vue.js
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的栏目导航程序
2006/10/09 PHP
php查看session内容的函数
2008/08/27 PHP
php操作XML、读取数据和写入数据的实现代码
2014/08/15 PHP
PHP利用正则表达式将相对路径转成绝对路径的方法示例
2017/02/28 PHP
Laravel 自带的Auth验证登录方法
2019/09/30 PHP
2020最新版 PhpStudy V8.1版本下载安装使用详解
2020/10/30 PHP
Javascript四舍五入Math.round()与Math.pow()使用介绍
2013/12/27 Javascript
JS常用表单验证方法总结
2014/05/22 Javascript
JavaScript DOM操作表格及样式
2015/04/13 Javascript
详解JavaScript中的表单验证
2015/06/16 Javascript
jquery实现带渐变淡入淡出并向右依次展开的多级菜单效果实例
2015/08/22 Javascript
TypeScript Type Innference(类型判断)
2016/03/10 Javascript
checkbox批量选中,获取选中项的值的简单实例
2016/06/28 Javascript
详细总结Javascript中的焦点管理
2016/09/17 Javascript
浅析JavaScript中的特殊数据类型
2017/12/15 Javascript
JS中Object对象的原型概念基础
2018/01/29 Javascript
js实现金山打字通小游戏
2020/07/24 Javascript
python 正则式使用心得
2009/05/07 Python
python递归函数绘制分形树的方法
2018/06/22 Python
python中将两组数据放在一起按照某一固定顺序shuffle的实例
2019/07/15 Python
解决pycharm 安装numpy失败的问题
2019/12/05 Python
python 基于selenium实现鼠标拖拽功能
2020/12/24 Python
python中zip()函数遍历多个列表方法
2021/02/18 Python
香港永安旅游网:Wing On Travel
2017/04/10 全球购物
美国韩国化妆品和护肤品购物网站:Beautytap
2018/07/29 全球购物
Kipling澳洲官网:购买凯浦林包包
2020/12/17 全球购物
学生拾金不昧表扬信
2014/01/21 职场文书
幼儿园消防演练方案
2014/02/13 职场文书
团日活动总结
2014/04/28 职场文书
小学先进集体事迹材料
2014/05/31 职场文书
酒店员工培训方案
2014/06/02 职场文书
电大奖学金获奖感言
2014/08/14 职场文书
先进个人评语大全
2015/01/04 职场文书
导游词范文
2015/02/13 职场文书
学风建设主题班会
2015/08/17 职场文书
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
2022/04/13 Java/Android