Javascript中的作用域和闭包
很多人都在说自己对闭包的理解,有的对我理解闭包很有帮助,有的我看了还是没能够理解,以下是我自己学到现在对作用域和闭包的理解,可能还比较浅显,希望给跟我一样在学习JavaScript的人们一点点帮助,也希望大家不吝指教!
先要了解全局变量和局部变量。
在最外层直接声明的变量,就叫全局变量。全局变量(或函数),在全局的任何地方都可以使用(作用范围是全局)。
在函数中声明的变量,叫做该函数的局部变量。局部变量(或函数),只在该函数内部起作用,外边访问不到。
1)函数内部可以直接读取全局变量
1 var a = 1;2 function fn(){3 alert(a);4 }5 fn(); // 1
2)函数外部无法读取函数内的局部变量
1 function fn(){2 var a = 1;3 }4 alert(a); //报错 a is not defined
3)函数内部声明变量的时候,一定要加上var,否则实际上是声明了一个隐式全局变量
1 function fn(){2 a = 1;3 }4 fn();5 alert(a); // 1
4)
1 function fn1(){2 var a = 1;3 function fn2(){ //函数fn2就是闭包4 alert(a);5 }6 return fn2();7 }8 fn1(); // 1
函数fn2被嵌套在函数fn1内,这时fn1内部的所有局部变量,对fn2都是可见的。但是反过来fn2内的局部变量对fn1都是不可见的。既然fn2可以读取fn1中的局部变量,那么只要把fn2作为返回值,我们就可以在fn1外部读取它的内部变量了。
JavaScript中的闭包,就是定义在一个函数内部的子函数,可以使用其父函数的局部变量。
1 function fn(b){2 return function(){3 b++;4 alert(b);5 }6 }7 var a = fn(0);8 a(); // 1
闭包除了可以读取其父函数的局部变量,还会让这些变量的值始终保存在内存中。消耗内存,所以不使用的局部变量需要删除。记得老师还说过在IE中可能导致内存泄露。
1 function fn1(){ 2 3 var a = 0; 4 b = function(){ 5 a++; 6 } 7 function fn2(){ 8 alert(a); 9 }10 return fn2;11 }12 13 var result = fn1();14 result(); // 015 b();16 result(); // 1
在这段代码中,result实际上就是闭包fn2函数。它一共运行了两次,第一次的值是0,第二次的值是1。这证明了,函数fn1中的局部变量a一直保存在内存中,并没有在fn1调用后被自动清除。
原因就在于fn1是fn2的父函数,而fn2被赋给了一个全局变量,这导致fn2始终在内存中,而fn2的存在依赖于fn1,因此fn1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
b = function(){a++;}
b是一个隐式全局变量,它的值是一个匿名函数,匿名函数本身也是一个闭包,所以可以在函数外边对函数内的局部变量进行操作。
作用域:
JS作用(读、写)域(空间、范围):可以理解为在什么样的范围内可以对数据进行读写。
alert(a);vara=0;//undefined
alert(a);a=0;//报错aisnotdefined
浏览器中的“JS解析器”:读到script开始执行代码之前至少分两步
1)“找一些东西”:varfunction参数
比如找到了咱们声明的变量a之后不会去读,是先给他一个值未定义a=undefined;
所有的变量,在正式运行代码之前,都提前赋了一个值:未定义
fn1=functionfn1(){alert(2);}
所有的函数,在正式运行代码之前,都是整个函数块
这一步叫做:JS的预解析
预解析的过程中遇到重名的会只留一个(有值的),如果都是有值的就把之前的替换掉。
解读代码的时候读到表达式会修改预解析的值。函数的声明不去修改表达式的值。
2)逐行解读代码:(修改仓库里的数据)
表达式(可以改变值的):=+-*/%++--!参数......Number()
当读到alert(a);时,不会往下去读,会先去它的仓库里去找有没有a
<script></script>是一个域,一个页面中可以有多个
自上而下,处理完一个script再处理下一个script。上边的script会影响到下边的script
所以,当一个script标签,需要调用另外一个script内的东西,那这个script一定要写在另外一个script下边
if和for的{}不是作用域,相当于是通透的里边的东西和定义到全局是一样的
作用域范围:
全局作用域:
1)最外层定义的全局变量、全局函数;
2)未定义直接赋值的变量即隐式全局变量;
3)所有window对象的属性。
以上都拥有全局作用域
局部作用域:function(函数作用域)
由里到外(局部有能力改外面的值)
几段代码:
1)
1 var a = 1;2 function fn1(){3 alert(a);4 var a = 2;5 }6 fn1(); //undefined7 alert(a); //1
刚开始还是找varfunction参数,function还是函数块;当逐行解读代码的时候,读到函数调用fn1(),函数是一个域,开启了一个新的作用域,又会发生两件事(预解析,逐行解读代码)“一个小仓库”局部的a跟外边全局的a没有关系
2)
作用域链:就近原则,先在子级找,在子级找不到会返回到父级去找
1 var a = 1;2 function fn1(){3 alert(a);4 a = 2;5 }6 fn1(); //17 alert(a); //2
3)参数本质上就是一个局部变量
1 var a = 1;2 function fn1(a){3 alert(a);4 a = 2;5 }6 fn1(); //undefined7 alert(a); //1
4)参数也是可以改变值的,函数调用从参数开始读
1 var a = 1;2 function fn1(a){3 alert(a);4 a = 2;5 }6 fn1(a); //17 alert(a); //1
5)局部是可以访问到外面的值的,所以可以改num的值。
1 var num = 0;2 function fn1(){3 num++;4 }5 fn1();6 alert(num); //1
从外面(全局)是找不到里面(函数/局部)的东西的,如果想找到至少有两种方法:
想要获取函数内的值:
1)用全局变量
1 var str = '';2 function fn1(){3 var a = 'abc';4 str = a;5 }6 fn1();7 alert(str); // abc
2)局部的函数调用
1 function fn1(){2 var a = 'abc';3 function fn2(a){4 alert(a);5 }6 fn2(a);7 }8 fn1();
新闻热点
疑难解答