首页 > 代码库 > js 闭包
js 闭包
初学者,本博文只是为自己学习而总结,参考者注意其中可能会有错误哦。
一 为什么用闭包
(一)访问局部变量
参考 http://www.jb51.net/article/24101.htm
一个内部函数除了可以访问自己的参数变量,还可以访问把它嵌套其中的父函数的参数和变量。
但是,一个外部函数却无法访问内部函数的变量。
function f1(){
var n=999;
}
alert(n); // error
我们有时候需要得到函数内的局部变量。因此,在函数的内部,再定义一个函数。
看下面这个例子
function f1(){
n=999; //f1不能访问f2的局部变量
function f2(){
alert(n); //f2也可以访问f1的变量 999
}
}
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量。
function f1(){
n=999;
return function (){
alert(n);
}
}
var result=f1();
result(); // 999
(二)安全性
假设我们有一个对象为student,其中有一个属性为name,要访问stu1,最简单的做法可能是
var Student = function (name){
this.name = name;
} //构造一个Student的构造函数
var stu1 = new Student("xiaoming");
//调用构造函数 创建一个实例stu1;
alert(stu1.name); //访问stu1的属性
}
但是这样,外部就可以任意访问stu1的内部属性,谈不上安全性。
我们可能会想到java中的做法,就是增加一个get set 方法
var Student = function (name){
this.name = name;
this.getName = function(){
return this.name;
}
} //构造一个Student的构造函数
var stu1 = new Student("xiaoming");
//调用构造函数 创建一个实例stu1;
alert(stu1.name); //访问stu1的属性 弹出 xiaoming
alert(stu1.getName()); //访问stu1的方法 弹出 xiaoming
非常遗憾,还是不ok,因为js无法像java一样设置 private name;public getName
(当然 去掉this.name = name,只保留getName方法好像在这个例子中可以达到目的,但毕竟不是长久之策)
但这个思路是可以的,下面就是如何模拟私有变量和公有方法。
我们可能会想到像上文一样,return一个函数(方法),这样,函数外部的可以访问这个return的内部函数(即方法),而不能访问这个函数本身的属性。
var Student = function (name){
var name = name;
//返回一个方法
return{
getName:function(){
return name;
}
}; } ;
//构造一个Student的构造函数
var stu1 = new Student("xiaoming");
//调用构造函数 创建一个实例stu1;
alert(stu1.name); //访问stu1的属性 错误
alert(stu1.getName()); //弹出小明
var stu2 = Student("xiaohong");
//调用构造函数 创建一个实例stu1;
alert(stu2.name); //错误
alert(stu2.getName()); //弹出小红
}
在这个例子中,我们调用Student ,返回一个包含getName方法的一个新对象,该对象的引用保存在stu1 stu2中,即使student已经返回来,但getName仍然可以访问student对象name属性
内部函数的生命周期要比外部函数的长
此外 还有来自知乎的另一个例子,不再赘述。
http://www.zhihu.com/question/19554716
另外还要说一点:上文返回的方法(getName)访问的并不是属性(name)的副本 而是其(name)本身。
举另外一个例子。
有一列表
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
要实现 点击0 弹出0 点击1 弹出1.。。。。
很容易写成这样
var ali = document.getElementsByTagName("li");
var i;
for(i=0;i<5;i++){
ali[i].onclick = function(){
alert(i);
}
}
但事实上无论点击哪一个,都会弹出4
这是因为,onclick函数访问的是i本身,而不是函数在构造是i的拷贝,所以每次都会弹出i;
可以这样修改
var ali = document.getElementsByTagName("li");
var i;
var help = function(i){
return function(){
alert(i);
}
}
for(i=0;i<5;i++){
ali[i].onclick = help(i);
}
}
循环之外创造一个辅助函数,让辅助函数返回一个绑定了i的值。
上面是大牛们给出的方法,但个人比较偏爱下面一种
var ali = document.getElementsByTagName("li");
var i;
for(i=0;i<5;i++){
ali[i].index = i;
ali[i].onclick = function(){
alert(this.index);
}
}
}
为列表添加一个index属性,同样可以达到目的。
最后 我们可以用闭包模拟模块。
“模块,一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数”
这样可以摒弃全局变量
--------------------------------------------------------------------------------------------------------
其他
一 变量作用域()
(一) “一个内部函数除了可以访问自己的参数变量,还可以访问把它嵌套其中的父函数的参数和变量
(二)”js与其他编程语言不同的时 它是函数作用域,函数内部的参数和变量在函数外面是不可见的,但在函数内部在任意时刻定义的变量,在任意地方都能访问。“
例
window.onload = function () {
for (var i = 0; i<5; i++){
alert("循环内部"+i);
}
alert(i); //在c中 会显示未定义 循环结束自动消除变量i 块型作用域
}
但是,在js中,没有块型作用域,变量是根据函数的存亡而存亡,所以会一次弹出 0 1 2 3 4 5;
因此 在函数顶端 最好声明所有需要用的变量
(三)”内部函数拥有比他的外部函数更长的生命周期“
引用 Douglas Crockford [1] :
之所以可能通过这种方式在 JavaScript 种实现公有,私有,特权变量正是因为闭包,闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
需要注意的一点时,内部函数访问的是被创建的内部变量本身,而不是它的拷贝。所以在闭包函数内加入 loop 时要格外注意。另外当然的是,闭包特性也可以用于创建私有函数或方法。
--------
关于为什么在 JavaScript 中闭包的应用都有关键词“return”,引用 JavaScript 秘密花园中的一段话:
闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。