前言
- 细阅此文章大概需要 10分钟左右
- 本篇中讲述了:
-
- 变量提升机制
-
- ES6新语法中的变量提升机制
-
- 匿名函数具名化
- 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
- 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
- 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!
- 欢迎转载,注明出处即可。
JS中的变量提升机制
- 声明【declare】:
+ 声明是创建变量的过程
```javascript
var a;
```
- 定义【defined】:
+ 定义是赋值的过程【将变量和值关联在一起】
```javascript
a = 10;
```
【ES3】变量提升:
- 在当前上下文中(无论是在全局/私有/块级),JS代码 自上而下执行之前,浏览器会提前处理一些事情【可理解为在词法解析阶段的一个环节】, 会把当前上下文中【带var/Funtion关键字的进行提前声明或定义】
-
带var的只会提前声明
-
带function的会提前声明加定义【函数声明的方式创建的函数】
- 声明函数名,并创建堆内存(堆内存中存储代码字符串)与之相关联
- 注意: 若使用函数表达式【又称函数字面量】创建函数,则会根据前面的var/let/const关键字来确定如何声明该函数,建议使用此方法创建函数,在变量提升阶段就只会声明函数名,不会创建堆内存并将地址与函数名关联,就不会出现在函数创建之前可以被执行的情况
-
【ES6】中的LET和const不会进行变量提升
- 在代码执行阶段,在let定义一个变量之前就使用它,浏览器会先看看下面的代码中有没有let声明该变量
- 若声明了则会报错【不能再let声明之前使用该变量】
- 否则报错【未定义】
-
在变量提升阶段声明过的变量,在代码执行阶段不会再重复声明。
-
基于VAR或FUNCTION,在【全局上下文】中声明的变量【全局变量】 ,会映射到GO(全局对象window)上一份,作为他的属性,而接下来对相应的进行修改时,一个修改另一个也会被修改。【严格模式也是如此】
var a = 10;
console.log(a);
console.log(window.a);
a = 20;
console.log(a);
console.log(window.a);
window.a = 30;
console.log(a);
-
在上下文中的变量提升阶段,若【在条件判断当中】,无论条件是否成立,都要进行变量提升
- FUNCTION在旧版本浏览器中【IE10以内】,会提前声明加定义
- FUNCTION在新版本浏览器中【IE10以上】,只会提前声明,不会再提前定义了
var foo = 1;
function bar(){
if(!foo){
console.log(foo);
var foo = 10;
}
console.log(foo);
}
bar();
-
【注意】自执行函数/匿名函数在当前上下文中不进行变量提升,仅在其执行时,在其私有上下文中进行变量提升
【ES6】块级作用域
- 在ES6中,基于let/const/function...创建变量
- 如果是出现在
【非函数】【非对象】 的大括号中 【大括号中出现let/const/function...都会被认为是块级作用域】 ,则这个大括号的范围内相当于一个块级作用域【在执行时会创建私有上下文】,
- {}中存在FUNCTION这个关键词会产生块级作用域也是新版浏览器才加入的
- 不止是在IF中,新版浏览器为了保证语义的准确性,如果函数出现在【除函数/对象】的大括号{}中,形成了块级作用域,则在【全局上下文中在变量提升阶段,对于此FUNCTION只声明不定义】
- 即使在大括号中同时出现
LET\CONST\FUNCTION 和VAR,虽然会产生块级作用域,但是对VAR不生效
- 【在块级作用域中VAR声明定义的变量会影响上级上下文,(上级上下文已存在的
【被function/var声明 定义的】变量会被修改,不存在的则会被同步创建一个)】
for(let i = 0; i<5;i++){
console.log(i);
}
-
因为【新版本浏览器】要兼容ES3/ES6,在【遇到if(块级作用域)】中存在function时会形成块级作用域【再执行函数本身时依然会有自己的私有上下文】
1. 在变量提升阶段,在EC(G)当中,只声明了该function a,【没有定义创建堆内存】【因为是新版本浏览器对于在if中的function a只声明不定义,而在【老版本浏览器】中在EC(G)下对函数声明+定义】
2. 而在代码执行时,进入到块级作用域的私有上下文时,也会进行变量提升,此时会对function a声明加定义【创建堆内存并与变量a关联】
3. 而在这个块级作用域的私有上下文的代码执行阶段,则不会在处理此行代码【做过的事情不会重复做】
4. 【重点】【只是在全局下的块级作用域,再多一层就没有映射了,...】:
- 但是浏览器会把
在这个块级作用域的私有上下文中【===此行代码之前的【包含此行代码】===】 所有对变量a的操作,映射给全局一份,以此来兼容ES3,而【===此行代码之后的【不包含此行代码】===】所有对变量a的操作,就只是对块级作用域的私有上下文中的a的操作了。
var a = 0;
if(true){
a = 1;
function a(){};
a = 21;
console.log(a);
}
console.log(a);
{
function foo(){};
foo = 1;
}
console.log(foo);
{
console.log(foo);
console.log(window.foo);
function foo(){};
foo = 1;
function foo(){};
console.log(foo);
}
console.log(foo);
var a = 12;
if(true){
console.log(a);
a = 13;
console.log(a);
function a(){};
a = 14;
console.log(a);
}
console.log(a);
-
进阶
var x = 1;
function func (x,y = function anonymous1(){x = 2}){
x = 3;
y();
console.log(x);
}
func(5);
console.log(x);
var x = 1;
function func(x,y =function anonymous1(){x = 2}){
var x = 3;
y();
console.log(x);
}
func(5);
console.log(x);
var x = 1;
function func(x,y =function anonymous1(){x = 2}){
var x = 3;
var y = function anonymous2(){x = 4};
y();
console.log(x);
}
func(5);
console.log(x);
-
函数体的大括号在某些情况下也会形成块级上下文
- 当一个
函数 同时满足两个条件,则在本身执行时会形成私有上下文的同时,会再形成一个私有块级上下文【将函数体{}包起来的看作】。【函数执行就有两个上下文了】
- 有形参赋值默认值
- 函数体中有声明过自己的私有变量【仅限VAR/LET/CONST】【形参赋值和私有声明的变量不同也会生成】
- 【FUNCTION只有在声明的名字和形参中的名字相同时,才会单独产生块级上下文】
- 【VAR/LET/CONST】无论形参和私有变量声明是否相同都会生成
【新形成的块级上下文】的【上级上下文】是【函数的私有上下文】,且会把【函数私有上下文】中的形参赋值传递进来,赋值给声明的私有变量