We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
以下概念:
#30 中有说到在js变量内存分配中,都是使用的堆,那什么时候使用栈呢?最常用的场景就是调用栈,即我们所熟知的call stack
call stack
js代码执行都是按照调用栈的规则后进先出去执行,程序初始化时,执行全局上下文,然后遇到其它函数,则push到栈中,如果当前没有其它调用栈,则执行最顶部的上下文。
我们知道多个EC就组成了Call Stack,而执行上下文由2部分组成
EC = { VO: { // ... 变量对象 }, scopeChain: [ VO(innerTest), ..., VO(Global) ] // 作用域链 }
执行上下文的生命周期,当函数被调用激活时,就开始它的生命周期
变量对象为上下文创建时创建,在上下文执行时,变量对象转化为活动对象Active Object
Active Object
// 创建阶段 变量对象 VO = { arguments: {}, foo: <foo reference>, // 表示foo的地址引用 a: undefined // 其它local变量声明 } // 执行阶段 活动对象 AO = { arguments: {...}, foo: <foo reference>, a: 1, this: Window }
变量对象的创建,依次经历了以下几个过程。 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。
变量对象的创建,依次经历了以下几个过程。
也就是说,先进行函数的声明,再进行变量的声明赋值。当进行变量的声明赋值时,如果该变量名已经被函数声明过,则跳过该声明不会覆盖。
console.log(foo) function foo() {} var foo = 1 // 结果是function
全局上下文的变量对象
// 以浏览器中为例,全局对象为window // 全局上下文 windowEC = { VO: Window, // 变量对象,就是window对象 scopeChain: {}, this: Window // this也是指向window }
作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。
// 例子 var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test(); // 执行上下文 innerTest innerTestEC = { VO: {...}, // 变量对象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链 }
我们知道,闭包的存在会将变量存在内存中,而不会被释放,从而垃圾回收始终将它是为存活对象而得不到回收。 那闭包中保存的是这个变量还是当前执行上下文的变量对象呢?答案是变量对象,但该对象中并不是所有的变量,只包含引用到的变量。
// 来个例子 var fn = null; function foo() { var a = 2; var b = 3; function innnerFoo() { var c = 1 console.log(innnerFoo.prototype) console.log(a, c); } fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn } function bar() { fn(); // 此处的保留的innerFoo的引用 } foo(); bar(); // 2
上面的例子断点可知,闭包为foo而不是innnerFoo,可以看到innnerFoo中的执行上下文中包含了Scope Chain和活动对象,而Scope Chain中包含了foo的变量对象中被引用而未释放的变量。
foo
innnerFoo
Scope Chain
Closure (foo) = { a: 2, innnerFoo: ƒ innnerFoo() }
即如下图所示
本文为阅读以下文章的总结 执行上下文 变量对象 作用域链与闭包
本文为阅读以下文章的总结
The text was updated successfully, but these errors were encountered:
No branches or pull requests
以下概念:
1.Call Stack
#30 中有说到在js变量内存分配中,都是使用的堆,那什么时候使用栈呢?最常用的场景就是调用栈,即我们所熟知的
call stack
js代码执行都是按照调用栈的规则后进先出去执行,程序初始化时,执行全局上下文,然后遇到其它函数,则push到栈中,如果当前没有其它调用栈,则执行最顶部的上下文。
2.Excution Context
我们知道多个EC就组成了Call Stack,而执行上下文由2部分组成
执行上下文的生命周期,当函数被调用激活时,就开始它的生命周期
3.Variable Object
变量对象为上下文创建时创建,在上下文执行时,变量对象转化为活动对象
Active Object
也就是说,先进行函数的声明,再进行变量的声明赋值。当进行变量的声明赋值时,如果该变量名已经被函数声明过,则跳过该声明不会覆盖。
全局上下文的变量对象
4.Scope Chain 与 Closure
作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。
我们知道,闭包的存在会将变量存在内存中,而不会被释放,从而垃圾回收始终将它是为存活对象而得不到回收。
那闭包中保存的是这个变量还是当前执行上下文的变量对象呢?答案是变量对象,但该对象中并不是所有的变量,只包含引用到的变量。
上面的例子断点可知,闭包为
foo
而不是innnerFoo
,可以看到innnerFoo中的执行上下文中包含了Scope Chain
和活动对象,而Scope Chain
中包含了foo
的变量对象中被引用而未释放的变量。即如下图所示
5.疑点解析
The text was updated successfully, but these errors were encountered: