简介:众所周知JS是单线程、解释性语言(由解释器解释一句执行一句) 讲到js变量提升&函数提升就不得不提到js的编译运行机制来解答这个问题
文章均为个人原创, 搬运请附上原文地址感谢, 原文来自MasterYi博客
console.log(demo); // undefined 此处打印undefined 预编译过程中加入GO 赋值 undefined
var demo = 'demo'; // 解释执行时赋值 demo
console.log(demo, window.demo); // 打印 demo, demo . var声明的变量会加入window
var a = 1;
function test(){
console.log(a); // 打印 undefined 是因为在test执行时创建了AO, AO在预编译时加上了a属性赋值undefined
// 在执行读取变量的顺序 读AO -> AO有则返回无则 -> 读取GO, 有则返回, 无则抛异常
// 所以在这里打印的a的过程: 访问AO -> 读到了 a -> 返回a 所以打印了 undefined , 因为在当前AO中找到了就不去再往执行栈中寻找这个值
var a = 2;
console.log(a); // 打印2 解释执行时赋值了2所以打印2
}
test();
var a = 1;
if(true){
console.log(a); // 执行1
var a = 2;
console.log(a); // 执行2 var没块级作用域所以很正常
}
let b = 1;
if(true){
/**
* let和const不会有变量提升 抛出致命错误Uncaught ReferenceError: Cannot access 'b' before initialization , b没被声明就进行了操作,
* 大家会感到奇怪的一个点, 外部明明声明了let b, 我这里使用应该打印1才对, 因为js执行时认定的你的块级作用域里面存在b的声明, 但是你在声明执行前进行了使用所以会报错
* 两种情况就不会报错, 不在let b = 2;前进行引用 或者 不进行let b = 2; 的声明
*/
console.log(b);
let b = 2; // 假设此处不声明都会正常打印1, 此处声明的同时上方引用就会引发异常
console.log(b); // 上方使用去掉这里会正常打印2
}
testA(); // 打印A 函数提升
console.log(testB) // 打印undefined testB使用的函数表达式声明, 变量虽然提升了, 但赋值得等执行阶段
function testA(){
console.log(`A`)
}
var testB = function (){
console.log(`B`)
}
testB(); // 打印B 赋值为了 function 所以可以执行, 表达式在赋值之前不可作为函数执行 ( 未赋值前都是undefined ) 会抛出致命错误
test(); // B 执行test 不会报错因为函数在预编译过程便会加入GO对象, 函数名当做GO对象的属性名,值为函数体
function test(){
console.log(`A`)
}
test(); // B
function test(){
console.log(`B`)
}
var test = function (){
console.log(`C`)
}
test(); // C 此处执行打印C 因为 test 进行赋值, 并覆盖了之前的 B
var test = function (){
console.log(`D`)
}
test(); // D 此处执行打印D 因为 test 进行赋值, 并覆盖了之前的 C
/**
* 第五个例子的执行过程 (之所以写这种看着很憨的重复代码是为了大家更好的理解 js的真实执行过程, 避免遇到疑难杂症时摸不透这玩意咋变成这样了)
* 1. 预编译阶段:将testC 加入 GO 赋值undefined
* 2. 预编译阶段:将testD 加入 GO 赋值undefined 覆盖testC
* 3. 预编译阶段:将testA 加入 GO 赋值函数体 覆盖 testD
* 4. 预编译阶段:将testB 加入 GO 赋值函数体 覆盖 testA
* 5. 执行第一个 test (testB) 打印B
* 6. 执行第二个 test (testB) 打印B, 此处可能大家会有一个疑点,解释性语言解释到到了testA 为什么执行还是B, 因为全局函数的赋值在预编辑阶段就结束了, 后续再出现也不会覆盖
* 7. 执行 test = functionC 的赋值并覆盖GO对象中原来的 testB
* 8. 执行第三个 test (testC) 打印C
* 9. 执行 test = functionD 的赋值并覆盖GO对象中原来的 testC
* 10. 执行第四个 test (testC) 打印D
*/