JavaScript之闭包理解

JavaScript之闭包理解JavaScript之闭包理解闭包(Closure)是JavaScript学习过程中一个非常重要的问题闭包和执行上下文和作用域链有着千丝万缕的关系。闭包是指有权访问另一个函数作用域的变量的函数(JavaScript高级程序设计(第三版)P178)闭包是一个函数,对闭包的一个理解可以是所在的执行上下文已经出栈,但是仍然访问了其所在的执行上下文变量对象的函数这里所指的所在的执行上下文指的是闭包函数对应的执行上下文,而不是闭包本省所对应的执行上下文来一个简单的例子functionA(){ var

大家好,欢迎来到IT知识分享网。JavaScript之闭包理解"

JavaScript之闭包理解

闭包(Closure)是JavaScript学习过程中一个非常重要的问题

闭包和执行上下文和作用域链有着千丝万缕的关系。闭包是指有权访问另一个函数作用域的变量的函数(JavaScript高级程序设计(第三版)P178)

闭包是一个函数,对闭包的一个理解可以是所在的执行上下文已经出栈,但是仍然访问了其所在的执行上下文变量对象的函数

这里所指的所在的执行上下文指的是闭包函数对应的执行上下文,而不是闭包本省所对应的执行上下文

来一个简单的例子

function A(){ 
   
	var a = 2;
    function B(){ 
   
		console.log(a);
    }
    return B;
}
A()();

那么他的执行上下文栈行为是这样的

/*伪代码*/
// 代码执行时最先进入全局环境,全局上下文被创建并入栈
ECStack.push(globalContext);
// A 被调用,A 函数上下文被创建并入栈
ECStack.push(<A> functionContext);
// A 执行完毕,A 函数上下文出栈
ECStack.pop();
// B 被调用,B 函数上下文被创建并入栈
ECStack.push(<B> functionContext);
// B 执行完毕,B 函数上下文出栈
ECStack.pop();
// 代码执行完毕,全局上下文出栈
ECStack.pop();

我们都知道,JavaScript 拥有自动的垃圾回收机制,当一个值失去引用的时候,垃圾回收机制会根据特殊的算法找到它并将其回收。

函数的执行上下文在出栈后,其变量对象会失去引用等待被回收,而闭包的存在会阻止这一过程,因为闭包的作用域链包含了其所在执行上下文的变量对象。

通过上面的代码可以看出,在B函数执行的时候,A的上下文已经出栈了,按照JavaScript的垃圾回收机制,A上下文的变量对象失去引用后会被垃圾回收机制回收,但是由于B上下文作用域链包含了A上下文的变量对象,所以A上下文的变量对象不会被垃圾回收机制回收。

我们知道函数作用域是在函数被定义(声明)的时候确定的。每一个函数都会包含一个[[scope]]内部属性,在函数被定义的时候,该函数的[[scope]]属性会保存其上层上下文的变量对象,形成包含上层上下文变量对象的层级链。

那么刚刚的代码上下文应该是这样的,在创建的时候

B.[[scope]]=[AContext.VO,globalContext.VO]

在B被调用的时候,其执行上下文会被创建并入栈,此时会生成变量对象并将该变量对象添加进作用域链的顶端,并将[[scope]]添加进作用域链

BContext.Scope = [BContext.VO].concat(B.[[scope]])
=>
BContext.Scope = [BContext.VO,AContext.VO,globalContext.VO]

可见,B上下文的作用域链包含了A上下文的变量对象,并且由于B访问A中的变量,阻止了A上下文的变量对象被垃圾回收机制回收。

看一个面试题经常会遇到的一个关于闭包的很经典的题目

var arr = [];
for (var i = 0; i < 3; i++) {
    arr[i] = function () {
        console.log(i);
    };
}

arr[0]();//3
arr[1]();//3
arr[2]();//3

在 arr[0] 函数执行之前,我们可以知道,全局上下文的变量对象如下所示

globalContext = { 
   
	VO:{ 
   
		arr:[],
        i=3
    }
}

在 arr[0] 被调用执行时,其作用域链在函数上下文的创建阶段被创建,其作用域链如下

arr[0]Context = {
    Scope: [arr[0]Context.VO, globalContext.VO]
}

arr[0]函数会在自身变量对象中寻找i(arr总没有i),所以会向上找,找到全局上下文变量对象中的i,所以输出3

那么如何解决闭包

立即执行函数

var arr =  [];
for(var i = 0; i < 3; i++){ 
   
	arr[i] =  (function(j) { 
   
        return function(){ 
   
			console.log(j);
        }
    })(i)
}
arr[0]();//0
arr[1]();//1
arr[2]();//2

ES6中的let

var arr = [];
for (let i = 0; i < 3; i++) { 
   
    arr[i] = function () { 
   
        console.log(i);
    };
}

arr[0]();//0
arr[1]();//1
arr[2]();//2

setTimeout

也还可以利用setTimeout的第三个参数

    var arr = [];
    for(var i = 0; i < 3; i++){ 
   
        arr[i] = setTimeout(function(i){ 
   
            console.log(i);
        },0,i)
    }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/12851.html

(0)
上一篇 2024-03-10 14:00
下一篇 2024-03-10 14:26

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信