let和const命令

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-07-03

let

1.let声明的变量命令只在其所在的代码块内有效。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
// 变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i
// 每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。

// 把var换成let  输出6
// 因为当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6

你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值? 这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

2.let不存在变量提升

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

3.暂时性死区(TDZ)

只要块级作用域内存在let命令,它所声明的变量就“绑定”这个区域,不再受外部的影响。

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

// 在let命令声明变量tmp之前,都属于变量tmp的“死区”。
// 如果块级作用域内存在用let声明的变量 这let声明执行之前 访问该变量都会出错

// 在let之前的typeof也就没那么安全了
typeof x; // ReferenceError
let x;
// 没有let语句 则正常返回undefined

4.不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数

function func(arg) {
  let arg;
}
func() // 报错

function func(arg) {
  {
    let arg;
  }
}
func() // 不报错

5.为什么需要块级作用域

没有块级作用域会导致某些场景不合理:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量。

const

const声明一个只读的常量。一旦声明,就必须立即初始化,不能留到以后赋值,也不可重复声明。

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动

对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

global对象

  • 浏览器里面,顶层对象是window,但 NodeWeb Worker 没有window
  • 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self
  • Node 里面,顶层对象是global,但其他环境都不支持。

兼容示例:

var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};