html5中文学习网

您的位置: 首页 > 网站及特效实例 > javascript特效 » 正文

Javascript学习笔记之 函数篇(三) : 闭包和引用_基础知识_

[ ] 已经帮助:人解决问题

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

模拟私有变量VSgHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
function Counter(start) {VSgHTML5中文学习网 - HTML5先行者学习网
    var count = start;VSgHTML5中文学习网 - HTML5先行者学习网
    return {VSgHTML5中文学习网 - HTML5先行者学习网
        increment: function() {VSgHTML5中文学习网 - HTML5先行者学习网
            count++;VSgHTML5中文学习网 - HTML5先行者学习网
        },VSgHTML5中文学习网 - HTML5先行者学习网
        get: function() {VSgHTML5中文学习网 - HTML5先行者学习网
            return count;VSgHTML5中文学习网 - HTML5先行者学习网
        }VSgHTML5中文学习网 - HTML5先行者学习网
    }VSgHTML5中文学习网 - HTML5先行者学习网
}VSgHTML5中文学习网 - HTML5先行者学习网
var foo = Counter(4);VSgHTML5中文学习网 - HTML5先行者学习网
foo.increment();VSgHTML5中文学习网 - HTML5先行者学习网
foo.get(); // 5VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

这里 Counter 返回两个闭包:函数 increment 和 get。这两个函数一直保持着对 Counter 作用域的访问,因此它们能一直访问到定义在 Counter 作用域的变量 count。VSgHTML5中文学习网 - HTML5先行者学习网

私有变量的工作机制VSgHTML5中文学习网 - HTML5先行者学习网

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

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
var foo = new Counter(4);VSgHTML5中文学习网 - HTML5先行者学习网
foo.hack = function() {VSgHTML5中文学习网 - HTML5先行者学习网
    count = 1337;VSgHTML5中文学习网 - HTML5先行者学习网
};VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

上面的代码不会改变 Counter 作用域内的 count 变量值,因为 hack 没有在 Counter 内定义。上面这段代码只会创建或者覆盖全局变量 count。VSgHTML5中文学习网 - HTML5先行者学习网

循环内的闭包VSgHTML5中文学习网 - HTML5先行者学习网

一个最容易犯的错误就是在循环内使用闭包。VSgHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) {VSgHTML5中文学习网 - HTML5先行者学习网
    setTimeout(function() {VSgHTML5中文学习网 - HTML5先行者学习网
        console.log(i);  VSgHTML5中文学习网 - HTML5先行者学习网
    }, 1000);VSgHTML5中文学习网 - HTML5先行者学习网
}VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

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

避免引用错误VSgHTML5中文学习网 - HTML5先行者学习网

为了复制循环中变量的值,最好的方式是在外层加一个匿名的立刻执行函数。VSgHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) {VSgHTML5中文学习网 - HTML5先行者学习网
    (function(e) {VSgHTML5中文学习网 - HTML5先行者学习网
        setTimeout(function() {VSgHTML5中文学习网 - HTML5先行者学习网
            console.log(e);  VSgHTML5中文学习网 - HTML5先行者学习网
        }, 1000);VSgHTML5中文学习网 - HTML5先行者学习网
    })(i);VSgHTML5中文学习网 - HTML5先行者学习网
}VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

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

还有另外一个方法可以实现同样的效果,就是在 setTimeout 内的匿名函数中再返回一个匿名函数:VSgHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) {VSgHTML5中文学习网 - HTML5先行者学习网
    setTimeout((function(e) {VSgHTML5中文学习网 - HTML5先行者学习网
        return function() {VSgHTML5中文学习网 - HTML5先行者学习网
            console.log(e);VSgHTML5中文学习网 - HTML5先行者学习网
        }VSgHTML5中文学习网 - HTML5先行者学习网
    })(i), 1000)VSgHTML5中文学习网 - HTML5先行者学习网
}VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

此外,通过 bind 方法也可以实现。VSgHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
VSgHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) {VSgHTML5中文学习网 - HTML5先行者学习网
    setTimeout(console.log.bind(console, i), 1000);VSgHTML5中文学习网 - HTML5先行者学习网
}VSgHTML5中文学习网 - HTML5先行者学习网
VSgHTML5中文学习网 - HTML5先行者学习网

文章最后我们来总结下:VSgHTML5中文学习网 - HTML5先行者学习网

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

(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助