1、变量的做用域 javascript
要理解闭包,首先必须理解Javascript特殊的变量做用域。变量的做用域就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量。
Js代码:
var n = 100;
function f1(){
alert(n);
}
f1(); // 100
另外一方面,在函数外部天然没法读取函数内的局部变量。
Js代码:
function f1(){
var n = 100;
}
alert(n); // error
这里有一个地方须要注意,函数内部声明变量的时候,必定要使用var命令。若是不用的话,你实际上声明了一个全局变量!
Js代码:
function f1(){
n = 100;
}
f1();
alert(n); // 100
java
2、如何从外部读取局部变量? 安全
出于种种缘由,咱们有时候须要获得函数内的局部变量。前面已经说过了,正常状况下,这是办不到的,只有经过变通方法才能实现。那就是:在函数的内部,再定义一个函数。
Js代码:
function f1(){
n = 100;
function f2(){
alert(n); // 100
}
}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的全部局部变量,对f2都是可见的。可是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式做用域”结构(chain scope),子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立。既然box2能够读取box1中的局部变量,那么只要把box2做为返回值,咱们不就能够在box1外部读取它的内部变量了吗!
Js代码:
function box1(){
n = 100;
function box2(){
alert(n);
}
return box2;
}
var result = box1();
result(); // 100 注意调用格式,也能够直接写成box1()()
闭包
3、闭包的概念 函数
上一节代码中的box2函数,就是闭包。各类专业文献上的“闭包”(closure)定义很是抽象,很难看懂。因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成“定义在一个函数内部的函数”。因此,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。
到底什么是闭包?”官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(一般是一个函数),于是这些变量也是该表达式的一部分。可是对于咱们初学者来讲,这个解释真TM坑爹!要理解闭包,仍是代码最有说服力啊,上代码:
function funcTest(){
var tmpNum=100; //私有变量
//在函数funcTest内定义另外的函数做为funcTest的方法函数
function innerFuncTest(){
alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum
}
return innerFuncTest; //返回内部函数
}
//调用函数
var myFuncTest=funcTest();
myFuncTest();//弹出100
如今咱们能够这么理解:“闭包”,在函数体内定义另外的函数做为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量,当其中一个这样的内部函数在包含它们的外部函数以外被调用时,就会造成闭包. 性能
4、闭包的用途 学习
闭包能够用在许多地方。它的最大用处有两个:一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。
Js代码:
function f1(){
var n = 100;
nAdd=function(){
n += 1
}
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); // 100
nAdd();
result(); // 101 this
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是100,第二次的值是101。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另外一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,所以 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,能够在函数外部对函数内部的局部变量进行操做。 spa
5、使用闭包的注意点 prototype
1)因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。
2)闭包会在父函数外部改变父函数内部变量的值。因此,若是你把父函数看成对象(object)使用,把闭包看成它的公用方法(Public Method),把内部变量看成它的私有属性(private value),这时必定要当心,不要随便改变父函数内部变量的值。
6、JS的垃圾回收原理
(1)、在javascript中,若是一个对象再也不被引用,那么这个对象就会被GC回收;
(2)、若是两个对象互相引用,而再也不被第3者所引用,那么这两个互相引用的对象也会被回收。
7、思考题
若是你能理解下面代码的运行结果,应该就算理解闭包的运行机制了。
Js代码:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
JavaScript闭包例子
function outerFun(){
var a = 0;
function innerFun(){
a++;
alert(a);
}
}
innerFun()
上面这段代码是错误的.innerFun()的做用域在outerFun()内部,所在outerFun()外部调用它是错误的。
改为以下,也就是闭包。
Js代码:
function outerFun(){
var a = 0;
function innerFun(){
a++;
alert(a);
}
return innerFun; //注意这里,添加一个return语句
}
var obj = outerFun();
obj(); //结果为1
obj(); //结果为2
var obj2 = outerFun();
obj2(); //结果为1
obj2(); //结果为2
-----------------------------------------------------------------------------------------------------------
如下部分还有待学习,尚未弄懂!就直接从 ‘脚本之家’ 剪切过来了!
PS 1、闭包内的微观世界
function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(); c();
若是要更加深刻的了解闭包以及函数a和嵌套函数b的关系,咱们须要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、做用域(scope)、做用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。
到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的做用域链包含了对函数a的活动对象的引用,也就是说b能够访问到a中定义的全部变量和函数。函数b被c引用,函数b又依赖函数a,所以函数a在返回后不会被GC回收。
当函数b执行的时候亦会像以上步骤同样。所以,执行时b的做用域链包含了3个对象:b的活动对象、a的活动对象和window对象,以下图所示:
如图所示,当在函数b中访问一个变量的时候,搜索顺序是:
小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的做用域是在定义函数时候就已经肯定,而不是在执行的时候肯定(参看步骤1和3)。用一段代码来讲明这个问题:
function f(x) { var g = function () { return x; } return g; } var h = f(1); alert(h());
这段代码中变量h指向了f中的那个匿名函数(由g返回)。
若是第一种假设成立,那输出值就是undefined;若是第二种假设成立,输出值则为1。
运行结果证实了第2个假设是正确的,说明函数的做用域确实是在定义这个函数的时候就已经肯定了。
PS 2、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而没法经过其余途径访问到,所以保护了i的安全性。
function Constructor(...) {
var that = this;
var membername = value;
function membername(...) {...}
}