js闭包小结

1.变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。eg:

alert(tmp);

var tmp=123;//未定义而不会报错

2.作用域链的一个经典例子:

name="lwy";

functiont(){

var name="tlwy";

function s(){

var name="slwy";

console.log(name);

}

function ss(){

console.log(name);

}

s();

ss();

}

t();

在上述代码中第一个输出slwy,第二个输出是tlwy,原因是:当执行js时,将创建函数的执行环境(调用对象),并将该对象置于链表开头,然后将函数的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显name是”slwy”。

但在执行ss()时,作用域链是:ss()->t()->window,所以name是”tlwy”。

说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。eg:

person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};

with(person.wife){

console.log(name);

}

with语句将person.wife添加到作用域链的部,所以输出的是:”lwy”;

with语句结束后,作用域链恢复正常。显然with语句存在着将对象copy并移动到作用域的头部的过程,所以产生了性能问题。

3.作用域链:简单的说作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,依次类推直至全局对象为止。当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的作用域链上找,一旦找到了变量,则不再继续。如果找到最后也没找到需要的变量,则解释器返回undefined。

4.闭包:了解了作用域链,再来看js的内存回收机制,一般来说,一个函数在执行的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了,对应的内空间也就被回收了。下次在执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。但如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的,并且这个内部函数又使用了外部函数的某些变量的话。这种内存回收机制就会出现问题。如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了。所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来。也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用后(例如被删除了,或者没有了指针),才会销毁这个闭包。即当没有任何一个闭包引用变量存在时,才会被下一次内存回收启动时所回收。

也就是说,有了闭包,嵌套的函数结构才可以运作,这也是符合我们预期的。eg:

varresult=[];

functionfoo(){

var i= 0;

for (;i<3;i=i+1){

result[i]=function(){

alert(i)

}

}

};

foo();

result[0]();// 3

result[1]();// 3

result[2](); // 3

这段代码中,程序员希望foo函数中的变量i被内部函数使用,并且能分别获得他们的索引,而实际上,只能获得该变量最后保留的值,也就是说,闭包中所有记录的自由变量,只是对这个变量的一个引用,而非变量的值,当这个变量被改变了,闭包里获取到的变量值,也会被改变。

解决的方法之一,是让内部函数在循环创建的时候立即被执行,并且捕捉当前的索引值,然后记录在自己的一个本地变量里。然后利用返回函数的方法,重写内部函数,让下一次调用的时候,返回本地变量的值,改进后的代码:

varresult=[];

functionfoo(){

var i= 0;

for (;i<3;i=i+1){

result[i]=(function(j){

return function(){

alert(j);

};

})(i);

}

};

foo();

result[0]();// 0

result[1]();// 1

result[2](); // 2

5.闭包就是函数内部的函数,本质上闭包就是将函数内部和函数外部连接起来的一座桥梁

6.闭包的作用:一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中

7.使用闭包注意的问题

1)  由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决的方法是,在退出函数之前,将不使用的局部变量全部删除。

2)  闭包会在父函数外部,改变父函数内部变量的值。所以,如果把父函数当做对象使用,把闭包当做它的公用方法,把内部变量当做它的私有属性,这时一定要小心,不要随便改变父函数变量的值。

8.闭包:是一个拥有许多变量和绑定了这些变量的环境表达式(通常是一个函数),因而这些变量也是该表达式的一部分,通俗的来说这句话是说所有javascript中的function都是一个闭包。不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。

当函数的内部函数被函数被函数外的一个变量引用的时候,就创建了一个闭包。所谓闭包就是再构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象函数反过来引用外层函数体中的临时变量。这使得只要目标对象在生存期内始终能够保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目标对象的方法内部却始终能引用到该变量的值,而且该值只能通过这种方法来访问。即使再次调用相同的构造函数。但只会生成新对象和方法,新的临时变量只是对应新的值,和上次那次调用的是各自独立的。

9.闭包的底层解释:对函数

当定义函数a的时候,js解释器会将函数a的作用域链(scopechain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。

当执行函数a的时候,a会进入相应的执行环境(excutioncontext)。

在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。

然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。

下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。

最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。

如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。

如果整个作用域链上都无法找到,则返回undefined。

上文提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) {

var g = function () { returnx; }

return g;

}

var h = f(1);

alert(h());

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。

假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

10.闭包的作用:

1)  保护函数内的变量安全。

2)  在内存中维持一个变量。

3)  通过保护变量的安全实现js私有属性和私有方法

时间: 2024-10-13 08:35:26

js闭包小结的相关文章

js闭包的作用

js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文 我们来看看闭包的用途.事实上,通过使用闭包,我们可以做很多事情.比如模拟面向对象的代码风格:更优雅,更简洁的表达出代码:在某些方面提升代码的执行效率. 1 匿名自执行函数 我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用

关于JS闭包,作者不详(转)

说明:本文由两篇文章结合而成,系从他人笔记中转过来的, 具体作者不详.因为觉得不错,遂共享之.如有侵权,立删致歉. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 fun

理解js闭包原理

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂.我的理解是,闭包就是能够读取其他函数内部变量的函数. 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数". 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁. 闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中. 怎么来理解这句话呢?请看下面的

javascript深入理解js闭包(看了挺多的,感觉这篇比较透彻)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

js闭包浅了解

js闭包浅理解 要理解闭包,得先知道js的变量作用域,在js中,有两种变量作用域: 全局作用域 局部作用域 一.在函数内可以访问全局变量 比如,下面的例子: <!--lang:js--> <script> var n = 100; function f1(){ console.log(n); } f1()//返回100 </script> 上面的例子很简单,下面是另一种情况. 二.在函数外无法读取函数内的局部变量 还是一个小例子: <!--lang:js-->

浅谈JS闭包中的循环绑定处理程序

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 前几天工作中写前端js代码时,遇到了遍历元素给它添加单击事件. (PS:之前也在<jQuery基础教程>第四版中看过讲循环绑定处理程序的内容,当时估计也没怎么用心看,所以没记起来.) 大神要是知道这类情况,可以关掉窗口,写这些主要是给像我一样的小白看的,谢谢! 先贴上错误的例子让大家

js闭包(closure),个人理解

一.闭包概念理解 各种专业文献上对js"闭包"(closure)定义非常抽象,贼难看懂.我的理解是,闭包就是能够读取某函数内部变量的函数.由于在Javascript语言中只有在函数内部的函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数".所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁. 二.用途 闭包可以用在很多地方.但它最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中. 三.使

js 闭包让函数只执行一次

js 闭包让函数只执行一次 <script>function once(f) {    var run;    return function() {        if (!run) {            run = true;            f.apply(this, arguments);        }    }}var g = once(function(x) {    alert(x)});g(1);g(2);g(3); </script> js 闭包让函

js闭包之我见

很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码: 1 function test(){ 2 for(var i=0;i<5;i++){ 3 window.onclick=function(){ 4 alert(i); 5 } 6 } 7 } 8 test() 原意是点击第一下弹出i的值0,点击第二下弹出i的值1,一直到第五次点击弹出4.想象是美好的,现实却不按常规出牌,无论点击多少次,alert出来的值都是5.后来接触了js闭包,才知道原来是它搞的鬼.看了不少相关书籍和博客后,