本文通过具体代码实例,深入解析JavaScript闭包的运行机制。重点讲解全局上下文与函数执行上下文的创建、压栈与销毁过程,阐明闭包如何维持对局部变量的引用,以及由此带来的内存开销。
JavaScript闭包原理与执行上下文详解
本文通过具体代码实例,深入解析JavaScript闭包的运行机制。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// 第一,函数作为返回值
function fn(){
var max = 10;
return function bar(x){
if(x > max) {
console.log(x);
}
};
}
var f1 = fn();
f1(15);
</script>
<script type="text/javascript">
// 第二,函数作为参数被传递
var max = 10;
fn = function(x){
if(x > max){
console.log(x);//15
}
};
(function(f){
var max = 100;
f(15);
})(fn);
</script>
<script>
function fn(){
var max = 10;
return function bar(x){
if(if > max){
console.log(x);
}
};
}
var f1 = fn();
max = 100;
f1(15);
</script>
</body>
</html>
首先,在代码执行前会生成全局上下文环境,并在执行过程中对其中定义的变量进行赋值,此时全局上下文处于活动状态。
在全局上下文中,变量max的初始值为undefined。
接着,执行var f1 = fn();语句时,调用fn()函数,系统会创建fn()的执行上下文环境并将其压入栈中,同时设为活动状态。
在fn()的执行上下文中,局部变量max被赋值为10。
当var f1 = fn();执行完毕后,fn()函数的调用理应结束。按照常规逻辑,其执行上下文环境应当被销毁。
然而,关键点在于:fn()函数返回的是一个内部函数。函数的特性之一是能够创建独立的作用域。
巧合的是,返回的这个内部函数体中引用了一个自由变量max,该变量指向fn()作用域下的执行上下文中的max值。
因此,这个max变量不能被销毁,否则内部函数在访问max时将无法找到对应的值。这意味着fn()的执行上下文环境必须保留,并继续存在于执行上下文栈中。
当代码执行到max = 100;时,全局上下文环境重新变为活动状态,但fn()的执行上下文环境依然驻留在执行上下文栈中。
执行完max = 100;后,全局上下文中的max变量被重新赋值为100。
此时,全局上下文环境中的max值为100,而fn()执行上下文环境中的max值仍保持为10。
随后,执行f1(15);语句,即调用内部函数bar(15)。系统会创建bar(15)的执行上下文环境,并将其设为活动状态。
在bar(15)执行期间,max作为自由变量,需要向创建bar函数的作用域链中查找,最终在fn()的作用域中找到了max,其值为10。
这里的核心逻辑在于:bar函数是在执行fn()时创建的。尽管fn()早已执行结束,但其执行上下文环境仍保留在栈中,因此bar(15)能够成功查找到max。如果fn()的执行上下文被销毁,max变量将无法被访问。
由此可见,使用闭包会增加内存内容的开销,这一点在上述过程中已十分明显。
最后,执行完f1(15);后,进入上下文环境的销毁过程,此处不再赘述。
闭包与作用域、执行上下文环境之间有着密不可分的关系,理解起来确实具有一定挑战性。
此外,闭包在jQuery等库中的应用非常广泛。无论是研究经典框架、类库,还是自行开发插件,掌握闭包、原型等基础理论都是必要的。否则,遇到BUG时可能因超出知识范围而难以排查。
更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript中json操作技巧总结》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。