变量提升的处理机制

167 阅读4分钟

变量提升

在**当前上下文中**(全局/私有/块级),JS代码自上而下执行之前,浏览器会提前处理一些事情(可以理解为词法解析的一个环节,词法解析一定发生在代码执行之前 )

  • 会把当前上下文中所有带VAR/FUNCTION关键字的进行提前的声明或者定义
 var a=10;
 声明declare:var a;
 定义defined:a=10;
  • 带VAR的只会提前的声明
  • 带FUNCTION会提前的声明定义

代码执行之前:全局上下文中的变量提升var a; 默认值是undefined

意义:能让我们在创建代码之前使用这个变量。

console.log(a); //=>undefined
var a = 12; //=>创建值12  不需要在声明a了(变量提升阶段完成了,完成的事情不会重新处理) a=12赋值
a = 13; //全局变量a=13
console.log(a); //=>13

函数两种表达方式的区别

function fn(){}

全局上下文中的变量提升 ; func=函数 函数在这个阶段赋值都做了

func();//
function func() {
	var a = 12;
	console.log('OK');
} 

var fn = function(){} (建议)

只声明,不赋值

var func = function () {
// 真实项目中建议用函数表达式创建函数,因为这样在变量提升阶段只会声明FUNC,不会赋值
console.log('OK');
};
func();

具名化

为了保证JS语法规范问题,一般在做这样处理的时候,会将匿名函数具名化

  • 虽说是起了名字,但是这个名字不能在外面访问=>也就是不会在当前当下文中创建这个名字①
  • 当函数执行,在形成的私有上下文中,会把这个具名化的名字做为私有上下文中的变量(值就是这个函数)来进行处理②
    • 应用:递归调用
var func = function AAA() {
console.log('OK');
console.log(AAA); //=>当前函数 ②
//AAA(); 递归调用   而不用严格模式下都不支持的 arguments.callee 了
};
//  ① AAA();  //=>Uncaught ReferenceError: AAA is not defined
func();

let/const 不存在变量提升

新版本浏览器多了向后兼容ES6的操作,会导致两次报错不同

console.log(a); //=>Uncaught ReferenceError: a is not defined
a = 13;
console.log(a);
console.log('OK'); //=>'OK'
console.log(a); //=>Uncaught ReferenceError: Cannot access 'a' before initialization  不能在LET声明之前使用变量
let a = 12;
a = 13;
console.log(a); 

GO关联

基于“VAR或者FUNCTION”在“全局上下文”中声明的变量(全局变量)会“映射”到GO(全局对象window)上一份,作为他的属性;而且接下来是一个修改,另外一个也会跟着修改

"let"和"const"不具备这个机制

EC(G):全局上下文中的变量提升
 *    不论条件是否成立,都要进行变量提升(细节点:条件中带FUNCTION的在新版本浏览器中只会提前声明,不会在提前的赋值了)
 *   [老版本]
 *      var a;
 *      func=函数;
 *   [新版本]
 *      var a;   全局上下文中声明一个a也相当于 window.a
 *      func;

console.log(a, func); //=>undefined undefined
if (!("a" in window)) {
//=>"a" in window 检测a是否为window的一个属性   !TRUE => FALSE
var a = 1;

function func() {}
}
console.log(a); //=>undefined

新版本浏览器下的不同机制

var a = 0;
if (true) {
    a = 1;
    function a() {};
    a = 21;
    console.log(a)
}
console.log(a);


// 新: 21    1
// 旧: 21    21

最新版本浏览器的特点

  • 需要向前兼容ES3/5 规范

      1. 判断体和函数体等不存在块级上下文,上下文只有全局和私有
      1. 不论条件是否成立,带function的都要声明+定义
  • 向后兼容ES6规范

      1. 存在块级作用域,大括号中出现 let/const/function... 都会被认为是块级作用域
      1. 不论条件是否成立,带function的只提前声明,不会提前赋值了。
  • 新版本浏览器处理方法:

因为要兼容ES3/ES6,function a在全局下声明过,也在私有下处理过,遇到此行代码,私有下不会再处理,但是浏览器会把当前代之前,所有对a的操作,映射给全局一份,以此兼容ES3,但是它后面的代码和全局没有任何关系了