关于闭包的问题

    xiaoxiao2021-03-25  98

    什么是闭包? 按照通俗的理解就是函数里面的函数,该函数能访问包含它的函数的变量。按照比较官方的理解:闭包是指有权访问另一个函数作用域的变量的函数。For Example:

    function funcA() { var a = 1, b = 2; return funcB(); function funcB() { // 注意,a 和 b 在本函数里根本没有定义,但是竟然能访问到 return a + b; } } var a = funcA(); // a 将会等于 3

    为什么funcB能访问到已经返回后funcA的变量呢?我们先了解一下函数被调用的过程。 当一个函数被调用后,会创建一个执行环境以及相应的变量对象、作用域链。在之前的一篇文章我曾经介绍过这三个概念。现在简单介绍一下。执行环境中定义了变量或函数有权访问的其他数据,决定了他们各自的行为,也就是说执行环境规定了你能访问什么,不能访问什么。那么这些规则保存在哪里呢?答案是在一个变量对象里面。这个变量保存着环境变量中所有的变量和函数。这个对象是不可以访问的。而作用域链是保证执行环境有权访问的所有变量和函数的有序访问。方向是就近原则。位于作用域前端的变量对象是本身的环境,比如这个环境是函数的话,函数的活动对象就是处于最前端的。以此类推下一个是直接包含环境的变量对象,再下一个是间接的包含环境。。。直到全局执行环境。比如:funcB能访问funcA的变量和函数,也能访问window对象的变量和函数。 还有一个事实是:全局环境的变量在函数执行始终是存在的,这个无可厚非,但是想funcA这个函数只有在函数执行过程中存在,在这之后包括他的执行环境和变量对象都会被销毁,那为什么funcB还能访问funcA的变量对象呢?这其实就是闭包的神秘之处了。 闭包可以将包含它的函数的活动对象(变量对象)添加到它的作用域链中,正是由于闭包的作用域链仍然在引用外部函数的活动对象,所以当funcA返回时,虽然它的执行环境的作用域链被销毁了,但是它的活动对象还在内存中。知道匿名函数会销毁.(因此使用过多的闭包可能占用过多的内存)

    闭包带来的副作用 1、闭包只能取得包含函数中任意变量的最后一个值。

    function createFunctions(){ var result =new Array(); for (var i =0;i<10;i++) { result[i] = function (){ return i; } } return result; } var result = createFunctions(); alert(result[0]()); //10

    表面上看result是返回一个0-9的数组,但resulti结果却是全部是10.原因是当createFunctions()返回时,变量的i值都是10了。 为什么会这样子呢? 我们先理解一下程序的思路,在上面的代码中其实每次执行闭包函数function (){ return i; 的时候,闭包函数都保存了createFunctions函数里面所有的变量,而且每次执行闭包函数的时候createFunctions函数里面的变量都会不变,当执行完createFunctions函数之后,createFunctions里面的i已经变为10了,又由于闭包函数不是立即执行,而是在createFunctions函数已经完全执行完之后执行的,所以最后每次闭包函数访问的都createFunctions完全执行完的变量(此时i已经是10了)。 解决办法: 既然是因为闭包函数不能立即执行的问题,那么我们就利用一个匿名函数通过立即执行把相应的i值立即放进闭包函数里面(此时对于闭包函数而言,i就是一个常数,也就是执行前已经拥有的值)看代码:

    function createFunctions(){ var result =new Array(); for (var i =0;i<10;i++) { result[i] = function(num){ return function(){ return num; } }(i); } return result; } var result = createFunctions(); alert(result[0]()); //0

    这样的话,闭包里面的num都是匿名函数立即执行时传进来的参数i; 2、this对象的问题 先来看一段代码:

    var name ="周运金"var object ={ name:"李佳家", getName:function(){ return function(){ this.name; } } } alert(object.getName()()); //"周运金"

    最后返回的周运金,也就是说this指针指向了window对象。但是根据之前的理解:当函数在全局变量中执行时,this等于window,当属被作为某个对象的内置方法时this指向该对象。 原因在于匿名函数具有全局性,因此this指针通常指向了window。这种情况下要this指向object也行,采用下面的做法:

    var name ="周运金"var object ={ name:"李佳家", getName:function(){ var that=this; return function(){ that.name; } } } alert(object.getName()()); //李佳家

    因为getName这个函数是object纯正的内置函数,所以可以访问this对象,然后将这个对象传给闭包。这也不算是高级的做法,其实有点硬来的意思了,哈哈.

    3、引起内存泄漏 这都是IE之前版本的对Jscript的垃圾回收机制惹的祸,这之间文章有提到过垃圾回收机制,常用的是标记清除。很少见的是IE之前版本的引用计数的垃圾机制。由于闭包在外部函数返回后还在使用外部函数的变量对象,所以此时外部函数的变量可能会被循环引用,导致引用次数无法减少,此时就会造成内存泄漏。比如:

    function ass(){ var element =document.getElementById('someElement'); element.onclick =function (){ alert(element.id); }; }

    解决方法:

    function ass(){ var element =document.getElementById('someElement'); var id=element.id; element.onclick =function (){ alert(id); }; element=null; }

    改进的代码有:将elment.id的一个副本保存在一个变量中,在闭包中引用该变量,消除了循环引用。最后还得手动清除element.因为即使必要不直接引用element,包含函数的活动对象也会保存一个引用。

    以上就是闭包与作用链的问题。

    转载请注明原文地址: https://ju.6miu.com/read-17121.html

    最新回复(0)