EC(G): Execution Context (Global) 全局执行上下文。代码提交后,在栈内存(ECStack执行环境栈)中形成的全局执行上下文。
-
VO(G):Variable Object(Global)全局变量对象
为了可以访问到存放在GO中的属性和方法,浏览器在全局执行上下文EC(G)中,默认声明了一个变量window,存放在VO(G)中,并让window指向GO的地址,后期便可以基于window访问内置的属性和方法。
- 我们经常把window称为全局对象
- 编写代码时,我们可以省略window 如window.alert() ⇒ alert();
GO:Global Object 全局对象 全局变量window对应的堆内存地址,用来存储内置的属性和方法。
var/function/let/const在全局执行上下文中的区别:
- 在全局执行上下文中,基于var/function声明的变量是存储在GO中的,相当于给window设置属性;
- 而基于let/const声明的变量是存储在VO(G)中的,他们才是根正苗红的全局变量。与window没有关系;
- 没有基于任何关键字修饰/声明的变量,其实是省略了”window.”,核心也是在GO中增加一个属性。
基于以上:
- 如果是window.xxx,直接到GO中找即可;
- 如果是直接输出一个变量,则先去VO(G)中找,有的话获取的是全局变量,如果没有则到GO中找,如果有,则是全局对象属性,如果没有则报错:xxx is not defined。
变量提升机制
-
在当前上下文中,代码执行之前,浏览器首先会把所有带var/function关键字的进行提前声明或者定义。
- var 只声明 不定义
- function 既声明又定义 注意:
- 函数在哪个执行上下文创建,它的作用域就是哪个执行上下文;
- 作用域是函数所处的执行上下文,在函数创建时确定,即其上级上下文;
- 函数私有执行上下文在函数执行时决定;
- 函数的声明定义,是先定义再声明,即先创建值[堆内存],再创建变量;
- 为函数开辟的堆内存中存储了:
- 函数的作用域scope <函数的执行上下文,函数的上级执行上下文>
- 代码字符串
- 键值对 name: ‘函数名’ length : ‘形参个数’
-
let/const/import/class声明的变量不存在变量提升。
-
私有作用域中的变量提升:
函数的底层运行机制:
代码运行到[函数执行]时,形成私有执行上下文EC(FN),私有变量对象AO(FN),在函数内代码执行前浏览器做了以下事情:
- 初始化作用域链
- 初始化this
- 初始化arguments
- 形参私有化并赋值
- 变量提升
- 代码执行
- 默认情况下,函数执行完,所形成的上下文会出栈释放掉
闭包:函数执行,会产生一个全新的私有上下文,保护里面的私有变量,不受外界的干扰,避免了全局变量污染,我们把函数的这种保护机制称之为闭包。
-
推荐使用函数表达式
functoin fn(){}````var fn = function(){}
[函数表达式] 的区别:- 函数表达式的执行只能在声明后执行,普通函数可以在声明定义前执行,区别在于变量提升
- 真实项目推荐使用函数表达式,确保函数执行只能在“创建函数代码”的下面,保证逻辑的严谨性。
-
匿名函数具名化[官方推荐规范]:
(function A() { A = 100; //无效操作 console.log(A); //函数本身 })(); 复制代码
- 设置的名字,并不会在所处上下文中进行声明;
- 在函数执行形成的私有上下文中,会把这个名字作为一个私有变量「存储到AO中」,变量值是当前函数本身「堆内存地址」; 并且默认情况下,对这个变量值进行修改是无效的;
- 但凡函数内部,这个名字被我们手动的声明过[例如:形参/var/let/const…],以我们声明的为准。则规则2无效,浏览器将权利移交给我们。
(function A() { console.log(A); //undefined var A = 100; console.log(A); //100 })(); 复制代码
匿名函数:自执行函数/函数表达式/回调函数….
匿名函数具名化的作用:
可以在函数内部基于这个名字访问到这个函数,这样就可以实现一些不具备的能力,如:递归
非严格模式下,也可通过arguments.callee访问当前匿名函数,arguments.callee.caller:存储函数在哪执行的,
“use strict” //开启JS严格模式,基于webpack打包后,都是严格模式,arguments.callee、arguments.callee.caller在严格模式下不能用
-
块级上下文 [ES6新增]
- 是在代码执行期间执行的,即碰到{}才形成相应的块级上下文
- 除函数和对象的大括号外,如:判断体、循环体、代码块,如果在大括号中出现了let/const/function/class 等关键词声明变量,则当前大括号会产生一个“块级私有上下文”,它的上级上下文就是其所处的上下文。
- let/const/funtion 会产生块级上下文,也会受到块级上下文的束缚
- var不产生,也不受块级上下文的影响
3. 函数在块级上下文中的特殊性【新版本与老版本的不同】
老版本:不会有块级上下文,所以在大括号(除函数和对象外)中出现fuction,还是保持原始的样子,变量提升阶段是声明+定义;
新版本,为了兼容ES5也可以兼容ES6,则全局下也要声明,私有块级上下文中也要声明。
1. 出现在“除函数/对象”以外的大括号中的function,在最开始变量提升阶段只声明
2. 会产生块级上下文,
3. **遇到函数声明的那串代码时候(分界点:将之前对该函数的值给上级上下文一份,此时该函数的上级上下文变量和该函数的私有变量拥有了同一个值【堆内存地址】,但他们始终是两个变量)**,会向该函数的全局变量指向函数对象地址,以后再对函数私有变量进行赋值,与该函数全局变量无关,只与函数私有变量有关。
复制代码
jsx { function foo() {} //**将之前对foo的操作同步给全局一份** foo = 1; function foo() {} //**也会将之前对foo的操作同步给全局一份,遇到一次改一次** } console.log(foo);// 1