var关键字

1.var声明作用域

使用var关键字声明的变量会成为包含它的函数的局部变量。这意味着当在函数中声明一个var变量时,当函数执行完便会被销毁。

function test(){
    var messsage = "hi";
}
test();
console.log(message); //出错!

特别的时当在函数内部声明变量时省略了var关键字,则可以创建一个全局变量。

function test(){
    messsage = "hi";
}
test();
console.log(message); //hi

2.声明提升

先看例子:

function foo(){
    console.log(age);
    var age = 16;
}

foo(); // undefined

这里之所以不会报错,==是因为var关键字声明的变量会自动提升到函数作用域的顶部==,同时给该变量赋一个初始值为undefined的值,这便是声明提升。

上面的例子等同于:

function foo(){
    var age;           //提前声明
    console.log(age);  //undefined
    age = 16; 
}

foo(); // undefined

var关键字的声明提升,也就是把所有变量声明都拉到函数作用域的顶部。同时var关键字也可以反复声明同一个变量。

function foo(){
    var age = 16;         
    var age = 26;     
    var age = 36;     
    console.log(age);  
}

foo(); // 36

let关键字

let关键字与var关键字作用差不多,但是有着非常重要的区别。 最明显的区别就是var声明的变量是函数作用域,而let声明的变量是块级作用域。

if(true){
    var age = 16;
}
console.log(age); //16


if(true){
    let name = "Tom";
}

console.log(name); //ReferenceError name没定义

let不允许在同一块级作用域中重复声明:

if(true){
    let age = 16;
    let age = 26;  //SyntaxError: Identifier 'age' has already been declared
}

1.暂时性死区

let与var的另一个重要的区别,就是let声明变量不会在作用域中被提升,也就是没得变量声明提升。

console.log(name); //undefined
var name = "Tom";

console.log(age); //SyntaxError
let age = 16;

在解析代码时,javascript引擎也会注意到出现在块后面的let声明,只不过在此之前不能以任何方式来引用let未声明的变量。 ==在let声明之前的执行瞬间被称为“暂时性死区”,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。==

2.全局声明

与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性,var声明则会。

var name = "Tom";
console.log(window.name); // Tom

let age = 16;
console.log(window.age); //undefined

不过,let声明仍然会在全局作用域中发生,相应的变量也会在页面的生命周期内存续。

let age = 16;
console.log(age);        //16
console.log(window.age); //undefined

3.条件声明

在使用var声明变量时,由于声明会被提升,所以javascript引擎会被冗余的声明在作用域的顶部合并成为一个声明,因为let时块级作用域的,所以不可能检查出前面是否已经使用let声明过同名变量,同时也不可以在没有声明的情况下声明它。

if (typeof name === "undefined") {
    let name = "Tom";
}

console.log(name);  // <empty string>

为此,对于let这个新的ES6声明关键字,不能依赖条件声明模式。

4.for循环中的let声明

在let出现之前,for循环定义的迭代变量会渗透到循环体外部:

for (var i = 0; i < 5; ++i){    //循环逻辑}console.log(i);  // 5

在let出现之后,let解决了这个问题,因为迭代变量的作用域仅局限于for循环内部:

for (let i = 0; i < 5; ++i){    //循环逻辑}console.log(i);  // ReferenceError 未定义

在使用var最常见的问题就是迭代变量的奇特声明与修改:

for (var i = 0; i < 5; ++i) {    setTimeout(() => {        console.log(i);     }, 0)}//你可能会以为输出 0 、1 、2 、3 、4 、5//实际上会输出 5 、5、5、5 、5、5

之所以会这样,是因为退出循环时,迭代变量保存的是导致循环退出的值:5,在执行超时逻辑时,所以i都是同一个值,因而输出的是同一个值。

==而let声明迭代变量时,javascript引擎会在后台为每一个迭代循环声明一个新的迭代变量。== 每一个setTimeout引用的都不是同一个变量实例。

for (let i = 0; i < 5; ++i) {    setTimeout(() => {        console.log(i);     }, 0)}//输出 0 、1 、2 、3 、4 、5

这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for-in , for-of循环。

const声明

const的行为与let基本相同,唯一一个重要的区别就是用它声明变量时必须同时初始化变量,且初始化之后值不能被修改。

const age = 16;age = 26;  //TypeError 给常量赋值

跟let一样,const也不允许重复声明:

const name = "Tom";const name = "Jack"; // SyntaxError

同时,==const的作用域也是块级的:==

const name = "Tom";if(true){    const name = "Jack";}console.log(name);   //Tom

要注意的是,const声明的限制只适用于它指向的变量的引用。换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制:

const person = {};person.name = "Tom";  // ok
最后修改:2021 年 06 月 30 日 09 : 29 PM
如果觉得我的文章对你有用,请随意赞赏