1,js的链式作用域
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript">
- //js链式作用域:父对象的所有变量,对子对象都是可见的,反之则不成立。
- function f1(){
- n=1;
- function f2(){
- var n2=11;
- alert("f2:n="+n);
- }
- f2();//1
- alert("n2:"+n2);//提示n2 is not defined。
- }
- f1();
- </script>
- </head>
- <body></body>
- </html>
2,闭包
上方代码中的f2函数就是闭包。
可以理解为:闭包就是可以访问其他函数内部变量的函数,例如f2访问f1,也理解为局部变量函数,因为f2就是f1的子函数。
3,闭包的用途
闭包的用处很多,最大通常有两个:一个是读取函数内部变量(f2读取f1),另一个是让这些变量的值始终保持在内存中。
第一种用途可以从上方代码中看到,看第二种用途:
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript">
- function f1(){
- var n=1;
- nAdd=function(){
- n+=1;
- }
- //f2把n保存在内存中
- function f2(){
- alert("f2:n="+n);
- }
- return f2;
- }
- var result=f1();
- result(); //n=1,内存中有n=1
- nAdd();//内存中n+1
- result(); //n=2
- </script>
- </head>
- <body></body>
- </html>
这个代码中可以看到,f2运行了两次,从第二次运行结果可以看到,n并没有在第一次运行之后被清楚。 原因在于:f1是f2的父函数,而f2被赋给了一个全局变量(result),这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。注意代码中的匿名函数nAdd,这个也是闭包,且相当于一个set函数,函数外部调用可以给局部变量赋值。
4,使用闭包注意
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
5,终极代码
(1)
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript">
- var name = "The Window";
- var object = {
- name : "My Object",
- getNameFunc : function(){
- return function(){
- return this.name;
- };
- }
- };
- alert(object.getNameFunc()()); //The Window
- </script>
- </head>
- <body></body>
- </html>
解决这个问题,首先清楚this指的是什么。可以在getNameFunc中alert一下,发现是window,就是全局环境,因为是全局变量,所以输出“The Window”,问题是为什么this指的是全局环境,参考:js中的this .
(2)
- <!DOCTYPE html>
- <html>
- <head>
- <script type="text/javascript">
- function outerFun(){
- var a=0;
- function innerFun(){
- a++;
- alert(a);
- }
- return innerFun; //注意这里
- }
- var obj=outerFun();
- obj(); //结果为1
- obj(); //结果为2
- var obj2=outerFun();
- obj2(); //结果为1
- obj2(); //结果为2
- </script>
- </head>
- <body></body>
- </html>