# 2. let与const

ES5及之前定义变量一般用var,ES6新增了let、const来声明变量,IE11+支持

var a = 1;
let b = 2;

# let与var的区别

var的特点:

  • 允许重复声明变量
  • 未经定义就可以使用(变量提升),默认为undefined
  • 没有块级作用域。for循环或if里面的变量,在外部仍然可以访问。
  • 函数或对象外部定义的变量都直接挂载到了window(self或this)全局变量上

let特性:

  • 不允许重复声明变量
  • 不存在变量提升,未经声明定义的变量不可以使用
  • 块级作用域 (作用域范围:最近的一个大括号范围内{} )。
  • 定义的对象不会挂载到window(self或this)全局变量上
  • 暂存性死区

# 非块级作用域的问题

# 内层变量可能会覆盖外层变量

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';  // 这里虽然在false循环里没有执行,但编译时由于var变量提升问题,tmp还是在函数内部被声明了。var tmp;改为let就可以避免这个问题
  }
}

f(); // undefined

# for循环变量泄露为全局变量

var s = 'hello';

for (var i = 0; i < s.length; i++) { 
  console.log(s[i]);
}
console.log(i); // 5

# for循环里函数变量问题

// 没有块级作用域的经典问题
var arr = []
for (var i = 0; i < 10; i++) {
  arr[i] = function() {
    return i 
  }
}
arr[0]() // 10

// 之前闭包的解决方法
var arr = []
for (var i = 0; i < 10; i++) {
  arr[i] = function(num) {
    return function() {
      return num;
    }
  }(i);
}
arr[0]() // 0

// 用let的解决方法
var arr = []
for (let i = 0; i < 10; i++) {
    arr[i] = function() {
        return i 
    }
}
arr[0]() // 0

# 块级作用域

{}大括号及for循环里用let声明的变量具有块级作用域,执行后会销毁。且不存在闭包变量的问题。

{
  let a = 1; // let声明的变量具有块级作用域
  var b = 1;
}
alert(a); // ReferenceError: a is not defined
alert(b); // 1

# let暂存性死区问题

ES6明确规定,如果区块中存在let和const命令,声明的对应变量是一个封闭的作用域。凡是在let声明之前使用变量,都会报错。

// 示例1
var tmp = 123
if (true) {         
    tmp = 'abc' // Uncaught ReferenceError: Cannot access 'tmp' before initialization   
    let tmp;
}

// 示例2
function bar(x = y, y = 2) { 
  return [x, y];
}
bar(); // 报错   当函数不传值时,y没定义就使用。Uncaught ReferenceError: Cannot access 'y' before initialization

// 示例3
let x = x;  // 报错Uncaught ReferenceError: Cannot access 'x' before initialization

# const变量

const特性与let类似。const用来声明一个只读的常量,声明后的值无法修改。理论上由于引用类型的地址一致。const声明一个引用类型。只要不改变地址,值是可以改变的。

const  a = {}
a.o = 1  // ok 
a = {}  // Uncaught TypeError: Assignment to constant variable.

const b = []  // ok
b.push(1,2,3) // ok
b = [] // Uncaught TypeError: Assignment to constant variable.

# 彻底冻结一个对象

如果想彻底冻结一个对象。可以使用Object.freeze()方法, 这样设置后对应的子属性也会无法修改

const foo = Object.freeze({})
foo.prop = 123  // 设置会不生效

// 彻底冻结对象及其子属性(需要递归)
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if (typeof obj[key] === 'object') {
      constantize(obj[key]);
    }
  });
};

# JS声明变量的6种方法

ES5及之前只有两种声明变量的方法:var、function ES6新增了let、const、import(模块化)、class(类) 4种声明变量的方法

# 顶层对象

顶层对象有window、self、this、global(Node)

  • var、function声明的全局变量,依旧是顶层对象的属性。
  • let、const、class命令声明的全局变量,不属于顶层对象的属性。
let a = 1;
window.a // undefined

var b = 1;
window.b // 1
上次更新: 2020/10/28 下午6:32:03