JavaScript原型、闭包与执行上下文核心机制解析

作者:青柠时间:2026-10-29 18:59:53浏览:193

本文详细解析JavaScript核心概念,包括闭包作为函数局部变量集合的持久化机制,以及原型对象在属性继承中的作用。重点阐述执行上下文(EC)的创建过程,涵盖全局、函数及Eval代码段的“准备工作”,如变量声明提升、this赋值及自由变量作用域的确定,帮助开发者深入理解JS内存管理与作用域规则。

相关系列文章:《谈谈我对JavaScript原型和闭包系列理解(随手笔记8)》与《谈谈我对JavaScript原型和闭包系列理解(随手笔记9)》。

闭包机制解析

什么是闭包

闭包(Closure)是静态语言中不具备的特性,但并非难以理解。简而言之,闭包的定义如下:

• 闭包是函数的局部变量集合,这些局部变量在函数返回后依然保持存在。

• 闭包意味着函数返回后,其“堆栈”不会释放;也可以理解为这些函数堆栈并非分配在栈上,而是分配在堆上。

• 当在一个函数内部定义另一个函数时,就会产生闭包。

原型与继承

什么是原型?

原型是一个对象,其他对象可以通过它实现属性继承。

是否任何对象都可以成为原型?

是。

哪些对象拥有原型?

所有对象默认情况下都拥有一个原型。由于原型本身也是对象,因此每个原型自身又有一个原型(唯一的例外是位于原型链顶端的那个默认对象原型)。

执行上下文与变量提升

执行上下文

每当控制器转向执行ECMAScript可执行代码时,就会进入一个执行上下文。

执行上下文(简称EC)是一个抽象概念,ECMA-262标准利用该概念来区分可执行代码(executable code)。

标准规范并未从技术实现角度精确定义EC的类型和结构,这属于具体ECMAScript引擎实现时需考虑的问题。

活动的执行上下文在逻辑上组成一个堆栈。堆栈底部始终是全球上下文(global context),堆栈顶部是当前(活动的)执行上下文。当EC类型的变量被推入或弹出时,堆栈随之修改。

--------------------------------------------------------------------------------

情况一:在一段JS代码真正逐句运行之前,浏览器已执行部分“准备工作”,其中包括变量声明而非赋值。变量赋值发生在赋值语句执行时。

情况二:在“准备工作”阶段,this是直接赋值的。

代码如下:

console.log(this) //Window

情况三:函数:函数表达式和函数声明。


 console.log(f1); //function f1() {}
function f1() {} //函数声明
console.log(f2); //undefined
var f2 = function() {}; //函数表达式 

“准备工作”总结:

• 变量、函数表达式————变量声明,默认赋值为undefined

• this————赋值

• 函数声明————赋值

这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。

--------------------------------------------------------------------------------

JavaScript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文。这个“代码段”分为三种情况--全局代码、函数代码、Eval代码。


 //全局代码段
<script type="text/javascript">
 //代码段...
</script>
//函数代码段
function fn(x) {
 console.log(x + 5);
}
var fn = new Function("x", "console.log(x + 5)");
//Eval代码段
eval('var x = 10');
(function foo() {
 eval('var y = 20');
})();
alert(x); //10
alert(y); //"y" is not defined
//因为eval涉及到安全问题(脚本注入),所以尽量不用。 

--------------------------------------------------------------------------------

在函数中,除了“准备工作”的几种情况,还会有其他数据


 function fn(x) {
 console.log(arguments); //[10]
 conosole.log(x); //10
}
fn(10); 

以上代码展示了在函数体的语句执行之前,arguments变量和函数参数都已经被赋值。

函数每被调用一次,都会产生一个新的上下文执行环境。因为不同调用那个就可能产生不同的参数。

函数在定义的时候(不是调用)就已经确定了函数体内部自由变量的作用域。


 var a = 10;
function fn() {
 console.log(a); //a是自由变量
}     //函数创建时,就确定了a要取值的作用域
function bar(f) {
 var a = 20;
 f(); //打印"10",而不是"20"
}
bar(fn); 

总结:

全局代码的上下文环境数据内容为:

• 普通变量(包括函数表达式),如:var a = 10 | ===> 声明(默认赋值undefined)

• 函数声明,如function fn() {} | ===> 赋值

• this | ===> 赋值

函数体

• 参数 | ===> 赋值

• arguments | ===> 赋值

• 自由变量的取值作用域 | ====> 赋值

通俗的定义:

在执行代码之前,把将要用到的所有变量都事先拿出来,有的直接赋值了,有的先用undefined占个位。

以上内容是小编给大家分享的JavaScript原型和闭包系列理解(随手笔记6)的全部叙述,希望大家喜欢。

作者最新文章