JS变量提升机制

变量提升机制

变量提升

当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带“VAR/FUNCTION”关键字的进行提前的“声明”或者“定义”,这种预先处理机制称之为“变量提升”

  • 声明: var a (默认undefined)
  • 定义: a = 12(定义其实就是赋值操作)

变量提升阶段

  • 带“VAR”的只声明未赋值
  • 带“FUNCTION”的声明和赋值都完成了

变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因此此时函数中存储的都是字符串而已)在全局作用域下声明的函数或者变量是“全局变量”,同理在私有作用域下声明的变量是“私有变量”【带VAR/FUNCTION的才是声明】

浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接跳过即可(因为在提升阶段就已经完成函数的赋值操作了)

私有作用域形成后,也不是立即执行代码,而是先进行变量提升(变量提升前,先形参赋值)

在ES3/ES5语法规范中,只有全局作用域和函数执行的私有作用域(栈内存),其他不生成栈内存

console.log(a);
var a = 12;
b = 13;
function sum(){
    var total = null;
}
sum()

->undefined

带VAR和不带VAR的区别

在全局作用域下声明一个变量,也相当于给WINDOW全局对象设置了一个属性,变量的值就是属性值(私有作用域中的声明的私有变量和WINDOW没啥关系)

console.log(a);
console.log(window.a)
console.log('a' in window);

-> undefined
-> undefined
-> undefined

全局变量和window中的属性存在“映射机制”一个改变另一个也改变

var a = 12;
console.log(a);
console.log(window.a);
a = 13;
console.log(window.a);
window.a = 14;
console.log(a);

-> 12
-> 12
-> 13
-> 14

可以使用属性名 in 对象,检测属性名是否隶属于这个对象

 console.log(a);
 console.log(window.a);
 console.log('a' in window);
 a = 12;
 console.log(a)
 console.log(window.a)

-> a is not defined
-> undefined
-> false
-> 12
-> 12

全局变量和WINDOW中的属性存在“映射机制”一个改变另一个也改变

console.log(a);
console.log(window.a);
console.log('a' in window);
a = 12;
consoel.log(a);
console.log(window.a);

-> 报错:a is not defined
注释掉报错的第一行
-> undefined
->false
-> 12
-> 12
-> 12

连续赋值运算下的VAR

var a = 12, b = 13;

这样写是带VAR的

var a = b = 12;

这样写b是不带VAR的

私有作用域域中带VAR和不带也有区别

  • 带VAR的在私有作用域变量提升阶段,都声明未私有变量,和外界没有任何的关系
  • 不带VAR不是私有变量,会向它的上级作用域查找,看是否为上级的变量,不是,继续向上查找,一直找到WINDOW为止(这种查找叫做:“作用域链”),也就是我们在私有作用域中操作的非私有变量,是一直操作别人的。
var a = 12, b = 12;
function fn(){
    console.log(a, b);
    var a = b = 13;
    console.log(a,b);
}
fn();
console.log(a,b);

-> undefined 12
-> 13 , 13
-> 12 ,13

变量提升阶段, 私有变量a被提升,声明a为私有变量并赋值为undefined
var a = b = 13;
把私有的A赋值为13,通过作用域链查找B并赋值为13

函数的提升

  • FUNCTION:声明的函数在变量提升阶段被声明
  • 匿名函数之函数表达式: var fn = function(){...},左边被提升,未执行到该行时,fn为undefined

在条件的变量提升

在当前作用域下,不管条件是否成立都要进行变量提升

  • 带VAR的还只是声明
  • 带FUNCTION的在老版本浏览器渲染机制下,声明+定义都处理,但为了迎合ES6中的块级作用域,新版浏览器对于在条件判断中的函数,不管条件是否成立,都只是先声明,没有定义,类似VAR

重名处理

带VAR和FUNCTION关键字声明相同的名字,这种也算是重名了(其实是一个FN,只是存储值的类型不一样)

var fn = 12;
function fn(){};

关于重命名的处理:
如果名字重复了,不会重新声明,但是会重新定义(重新赋值)(不管是变量提升还是代码执行阶段皆是如此)

LET

LET创建的变量不存在变量提升问题,且不会与window产生映射

在相同作用域中,基于LET不能声明相同名字的变量(不管用什么方式在当前作用于下声明了变量,再次使用LET创建变量都会)

虽然没有变量提升机制,但是在当前作用域代码自上而下执行之前,浏览器会做一个重复性检测:

自上而下查找当前作用域下所有变量,一旦发现有重复的,直接抛出异常,代码也不会在执行了

posted @ 2020-09-09 09:44  lemon-Xu  阅读(584)  评论(0编辑  收藏  举报