乐趣区

关于javascript:关于letconst不会造成变量提升的误解与纠正

在 JavaScript 中,必定不可避免的须要申明变量和函数,然而,有这么一种景象:

即便咱们在定义这个函数之前调用它,函数依然能够工作。即便咱们在定义这个变量之前调用它,代码也不会报错。

初学的时候,上网查资料,失去的论断是 变量晋升 ,对于过后的我来说, 变量申明提前 这个说法更好了解。对此,MDN 给出的解释是 这是因为在 JavaScript 中执行上下文的工作形式造成的

🆗,那咱们先简略聊聊 执行上下文

执行上下文(Execution Context)

执行上下文:英文直译,了解起来有点奇怪,能够简略了解成 以后代码的执行环境
每次当控制器转到可执行代码的时候,就会进入一个执行 执行上下文。它会造成一个作用域。JavaScript 中的运行环境大略包含三种状况。

  • 全局环境:JavaScript 代码运行起来会首先进入该环境;
  • 函数环境:当函数被调用执行时,会进入以后函数中执行代码;

因而在一个 JavaScript 程序中,必定会产生多个执行上下文。JS 引擎会以栈的形式来解决它们。

更多请见执行上下文详解

执行上下文的生命周期

执行上下文的生命周期分为三个阶段:

  • 创立阶段(函数被调用时触发)

    在这个阶段中,执行上下文会别离 创立变量对象,确定this 指向,以及其余须要的状态;

  • 代码执行阶段

    创立实现后,就会开始执行代码,会实现 变量赋值,以及执行其余代码;

  • 销毁阶段

    可执行代码执行结束之后,执行上下文 出栈 , 对应的内存空间失去援用,期待被回收;

tips:具体理解执行上下文极为重要,其中波及到变量对象,this 指向,垃圾回收等多个重要知识点

其中,创立变量对象与变量晋升密切相关

变量对象

{a: 10}

变量对象的创立,顺次经验以下几个过程:

一:建设 arguments 对象:查看以后上下文中参数,建设该对象下的属性与属性值;

二:查看以后上下文的函数申明,也就是应用 function 关键字申明的函数。在变量对象中以函数名建设一个属性,属性值指向 该地址所在内存 地址的援用

三:查看以后上下文中的变量申明,每找到一个变量申明,就在变量对象中以变量建设一个属性,属性值为undefined,const/let 申明的变量没有赋值,不能提前应用;

* const/let 申明的变量不能应用是因为没有赋值,不是因为没有申明;(误会)* 如果 var 变量与函数同名,则在这个阶段(执行上下文创立阶段),以函数值为准;在下一个阶段(执行上下文执行阶段),函数值会被变量值笼罩;(难点)

简略示例:

console.log(foo); // function foo
function foo() { console.log('function foo') }
var foo = 20;
// 上述的执行程序为
// 首先将所有函数申明放入变量对象中
function foo() { console.log('function foo') }

// 其次将所有变量申明放入变量对象中,然而因为 foo 曾经存在同名函数,此时以函数值为准,而不会被 undefined 笼罩
// var foo = undefined;

// 而后开始执行阶段代码的执行
console.log(foo); // function foo
foo = 20;

上述规定能够看出,function 申明会比 var 申明优先级更高一点。

// demo2
function test() {console.log(foo);
    console.log(bar);

    var foo = 'Hello';
    console.log(foo);
    var bar = function () {return 'world';}

    function foo() {return 'hello';}
}

test();

上例全局作用域中运行 test()时,test()的执行上下文开始创立。为了便于了解,咱们用如下模式示意:

// 创立过程
testEC = {
    // 变量对象
    VO: {},
    scopeChain: {}}

// 因为本文临时不具体解释作用域链,所以把变量对象专门提出来阐明

// VO 为 Variable Object 的缩写,即变量对象
VO = {arguments: {...},  // 注:在浏览器的展现中,函数的参数可能并不是放在 arguments 对象中,这里为了不便了解,我做了这样的解决
    foo: <foo reference>  // 示意 foo 的地址援用;函数申明比 var 申明优先级高
    bar: undefined
}

未进入执行阶段之前,变量对象中的属性都不能拜访!然而进入执行阶段之后,变量对象转变成流动对象,外面的属性便能拜访了,之后开始执行阶段的操作。

tips:变量对象和流动对象其实是同一个对象,只是执行上下文不同的生命周期;变量对象处于创立阶段,流动对象处于执行阶段;

// 执行阶段
VO ->  AO   // Active Object
AO = {arguments: {...},
    foo: 'Hello',      // 到执行阶段,foo 的值被笼罩
    bar: <bar reference>,
    this: Window
}

let/const

let/const 作为 ES6 新增关键字,能够造成块作用域。然而有一个问题,let/const 申明的变量,是否还会变量晋升

试验验证:

第一步:咱们间接应用一个未定义的变量

console.log(a);

报错信息显示 未定义

第二步:在 let 定义之前应用变量

console.log(a);
let a = 10;

报错信息显示 变量定义了,然而没有初始化:

论断:let/const申明的变量,依然会 提前被收集到变量对象中 ,但和var 不同的是,let/const 定义的变量,不会再这个时候给它赋值 undefined。

因为没有赋值,所以即便变量申明提前了,咱们也不能再赋值之前调用他,这就是咱们常说的 暂时性死区

参考资料🍓

1. 执行上下文详解
2.MDN 变量晋升
3. 变量对象详解

退出移动版