变量声明之let、const和var

ECMAScript变量是松散类型的,意思是变量可以用于保存任何类型的数据。有3个关键字可以声明变量:varconstlet。其中,var在ECMAScript的所有版本中都可以使用,而constlet只能在ECMAScript 6及更晚的版本中使用。

var声明

var声明作用域

关键的问题在于,使用 var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用 var 在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:

function test() {
var message = "hi"; // 局部变量
}
test();
console.log(message); // 出错! message在函数调用之后随即会销毁
复制代码

不过,在函数内定义变量时省 略 var 操作符,可以创建一个全局变量:

function test() {
message = "hi"; // 全局变量 }
test();
console.log(message); // "hi" 去掉之前的 var 操作符之后,message 就变成了全局变量。只要调用一次函数 test(),就会定义 这个变量,并且可以在函数外部访问到。
复制代码

var声明提升

所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。如下面的代码不会报错:

function foo() { 
    console.log(age); 
    var age = 26;
}
foo();  // undefined
复制代码

之所以不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:

function foo() {
    var age;
    console.log(age);
    age = 26; 
}
foo();  // undefined
复制代码

此外,反复多次使用 var 声明同一个变量也没有问题:

function foo() { 
    var age = 16;
    var age = 26;
    var age = 36; 
    console.log(age);
}
foo(); // 36
复制代码

let声明

let 跟 var 的作用差不多,但有着非常重要的区别。

区别:

  • let 声明的范围是块作用域,而 var 声明的范围是函数作用域。 块作用域 是函数作用域的子集,因此适用于 var 的作用域限制同样也适用于 let。
if (true) { 
    var name = 'Matt';
    console.log(name); // Matt
}
console.log(name) // 'Matt'


if (true) {
    let age = 26; 
    console.log(age); // 26
} 
console.log(age); // ReferenceError: age
复制代码
  • let不允许同一个块作用域中出现重复声明,会导致报错。
var name;
var name;

let age;
let age; //SyntaxError;标识符age已经声明过了
复制代码
  • let声明的变量不会在作用域中被提升。
// name 会被提升 
console.log(name); // undefined 
var name = 'Matt';

// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26;
复制代码

PS: 在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError。

  • 与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声 明的变量则会)。
var name = 'Matt'; 
console.log(window.name); // 'Matt'

let age = 26;
console.log(window.age); // undefined
复制代码

const声明

const 的行为与 let基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。

const age = 26;
age = 36; // TypeError: 给常量赋值
复制代码

const所说的一旦声明值就不能改变,实际上指的是:变量指向的那个内存地址所保存的数据不得改动

  • 简单类型(number、string、boolean):内存地址就是值,即常量(一变就报错)

  • 复杂类型(对象、数组等):地址保存的是一个指针,const只能保证指针是固定的(总是指向同一个地址),它内部的值是可以改变的(不要以为const就安全了!)

所以只要不重新赋值整个数组/对象,因为保存的是一个指针,所以对数组使用的push、shift、splice等方法也是允许的。

const person = {}; 
person.name = 'Matt'; // ok
复制代码

最后,总结一下它们的异同:

var和let/const的区别:

  1. 块级作用域
  2. 不存在变量提升
  3. 不可重复声明
  4. let、const声明的全局变量不会挂在window对象下面

const命令两个注意点:

  • let可以先声明稍后再赋值,而const在声明之后必须马上赋值,否则会报错
  • const 简单类型一旦声明就不能再更改,复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。

声明风格及最佳实践

  1. 不使用 var

用let代替var

  1. const 优先,let 次之

优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。

参考:
juejin.cn/post/684490…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享