JS笔记《let与const》

94 阅读4分钟

块级作用域

  • ES5只有全局作用域函数作用域,ES6新增了块级作用域
// var 定义
function f1() {
  var n = 5;
  if (true) {
    var n = 10;
  }
  console.log(n); // 10
  // 因为 var定义的不存在块级作用域,if中的变量 n会覆盖外层的 n
}

// let定义
function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5  
  // 两个块级作用域都定义了变量 n,打印时是在外部的块级作用域,所以指向的就是外部的块级作用域中定义的 n 
}

// 以下代码使用了五层的块级作用域,每一层都是一个单独的作用域。第四层无法读取第五层的内部变量
{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 报错
}}}};

let命令

  • ES6新增了let命令用来声明变量。但所声明的变量只在let命令所在的代码块内有效。
{
  let a = 10;
  var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
  • 在使用let声明迭代变量时,每次迭代循环都会创建一个新的块级作用域,变量仅在当前循环内部可见。
// 变量 i是 let声明的,当前的 i只在本轮循环有效,所以每一次循环的 i其实都是一个新的变量
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    // 输出的是循环时创建的那个块级作用域中的变量 i
    console.log(i);
  };
}
a[6](); // 6


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


// 使用闭包解决var声明的问题
var a = [];
  for (var i = 0; i < 10; i++) {
    // 将变量i保存到IIFE中,内部函数使用变量i,就会导致IIFE中的变量i被一直保存
    (function(j){
      a[i] = function(){
        console.log(j);
      }
    })(i);
  }

不存在变量提升

  • var声明的变量存在变量提升,即变量可以在声明前使用,值为undefined。而let命令声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(foo);  // 输出undefined
var foo = 2;

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

暂时性死区

  • 如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
if (true) {
  // TDZ开始
  tmp = 'abc';      // ReferenceError
  console.log(tmp); // ReferenceError

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

  tmp = 123;
  console.log(tmp); // 123
}
  • 暂时性死区意味着typeof不再是一个百分之百安全的操作。
typeof x;  // ReferenceError 因在定义之前调用的,形成了暂时性死区,所以报错
let x;

// 如果一个变量根本没有被声明,使用`typeof`反而不会报错
typeof xxxx;   // 'undefined'

不允许重复声明

  • let不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
  let a = 10;
  var a = 1;
}


// 报错
function func() {
  let a = 10;
  let a = 1;
}

const命令

  • const声明一个只读常量,只在声明所在的块级作用域内有效。一旦声明就必须立即初始化,且常量的值不能改变。
const PI = 3.1415;
PI // 3.1415

PI = 3; // TypeError: Assignment to constant variable.

const foo; // SyntaxError: Missing initializer in const declaration

不存在变量提升

  • let命令。

暂时性死区

  • let命令。

不允许重复声明

  • let命令。

常量的本质

  • 并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
const a = 1;
a = 2;  // SyntaxError: Identifier 'a' has already been declared

const obj = {};
obj.a = 1;  // 正常执行,因为变量obj指向的内存地址并没有改变,而是内存地址指向的数据变化了

obj = [];   // TypeError: Assignment to constant variable. 
// 修改了 obj指向的内存地址,所以报错

ES6声明变量的6种方法

  • ES5中的var 、function,ES6中的let、const、import、class

顶层对象的属性

  • ES6 为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象window的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
var a = 10;
function test(){}

console.log(window.a);  // 10
console.log(window.test)// ƒ test(){}


let b = 20;
console.log(window.b);  // undefined

globalThis对象

  • ES2020在语言标准的层面,引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this