gdjlc

培养良好的习惯,每天一点一滴的进步,终将会有收获。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

var 声明与变量提升

使用 var 关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)。这就是变量提升,不过变量的声明虽然被提升到了顶部,但初始化工作还保留在原处。

function getValue(condition) {
    if (condition) {
        var value = "blue";
        // 其他代码
        return value;
    } else {
        // value 在此处可访问,值为 undefined
        return null;
    }
    // value 在此处可访问,值为 undefined
}

块级声明

块级声明让所声明的变量在指定块的作用域外无法被访问。
块级作用域在如下情况被创建:
1. 在一个函数内部
2. 在一个代码块(由一对花括号包裹)内部

let声明

let 声明的语法与 var 的语法一致,但会将变量的作用域限制在当前代码块中,并且不会被提升到当前代码块的顶部。

function getValue(condition) {
    if (condition) {
        let value = "blue";
        // 其他代码
        return value;
    } else {
        // value 在此处不可用
        return null;
    }
    // value 在此处不可用
}

禁止重复声明

如果一个标识符已经在代码块内部被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。例如:

var count = 30;
// 语法错误
let count = 40;
但是在嵌套的作用域内使用 let 声明一个同名的新变量,则不会抛出错误
var count = 30;
// 不会抛出错误
if (condition) {
    let count = 40;
    // 其他代码
}

常量声明

使用 const 声明的变量会被认为是常量,它们的值在被设置完成后就不能再被改变。
正因为如此,所有的 const 变量都需要在声明时进行初始化。
常量声明与 let 声明一样都是块级声明,在声明它们的语句块外部无法访问,并且声明也不会被提升。
const 声明在同一作用域(全局或是函数作用域)内定义一个已有变量时也会和let一样抛出错误。
如果 const 声明的是对象,阻止的是对绑定值的修改,不会阻止对成员值的修改。

const person = {
    name: "Nicholas"
};
// 正常
person.name = "Greg";
// 抛出错误
person = {
    name: "Greg"
};

暂时性死区

使用 let 或 const 声明的变量,在达到声明处之前都是无法访问的,试图访问会导致一个引用错误,即使在通常是安全的操作时(例如使用 typeof 运算符)。

if (condition) {
    console.log(typeof value); // 引用错误
    let value = "blue";
}

JS 引擎检视接下来的代码块并发现变量声明时,它会在面对 var 的情况下将声明提升到
函数或全局作用域的顶部,而面对 let 或 const 时会将声明放在暂时性死区(temporal dead zone,TDZ )内。任何在暂时性死区内访问变量的企图都会导致运行时错误。
然而,可以在变量被定义的代码块之外对该变量使用 typeof。

console.log(typeof value); // "undefined"
if (condition) {
    let value = "blue";
}

当 typeof 运算符被使用时, value 并没有在暂时性死区内,因为这发生在定义 value 变量的代码块外部。

循环中的块级绑定
下面例子用var 声明导致变量提升,结果i为10。

for (var i = 0; i < 10; i++) {
}
console.log(i);//10

如果把var改成let,则会抛出错误。

for (var i = 0; i < 10; i++) {
}
console.log(i);//抛出错误:i is not defined。

因为let定义的变量i仅在for循环内部可用。

循环内的函数

var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func(); // 输出数值 "10" 十次
});

因为变量 i 在循环的每次迭代中都被共享了,意味着循环内创建的那些函数都拥有对于同一变量的引用。
修正这个问题,在循环内使用立即调用函数表达式(IIFEs),以便在每次迭代中强制创建变量的一个新副本:

var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
            console.log(value);
        }
    } (i)));
}
funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
});

变量 i 被传递给 IIFE,从而创建了 value 变量作为自身副本并将值存储于其中。
使用 let 与 const 的块级绑定可以简化这个循环

var funcs = [];
for (let i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
})

在循环中let 声明每次都创建了一个新的 i 变量,因此在循环内部创建的函数获得了各自的 i 副本。
如果上面let改成const,则会在一次迭代后因为试图改变i的值而抛出错误。
let这种方式在 for-in和 for-of 循环中同样适用:

var funcs = [],
object = {
    a: true,
    b: true,
    c: true
};
for (let key in object) { //let可以改成const
    funcs.push(function() {
        console.log(key);
    });
}
funcs.forEach(function(func) {
    func(); // 依次输出 "a"、 "b"、 "c"
});

const不能用于常规for循环,但可以用于for-in 和 for-of,因为循环为每次迭代创建了一个新的变量绑定,而不是试图去修改已绑定的变量的值。

块级绑定新的最佳实践

因为大部分变量在初始化之后都不应当被修改,所以在默认情况下使用 const 、并且只在知道变量值需要被更改的情况下才使用 let 。

posted on 2021-03-10 17:13  gdjlc  阅读(71)  评论(0编辑  收藏  举报