《ES6标准入门》—【let const 解构赋值】

《ES6标准入门》—【let const 解构赋值】

xiaolu
2022-02-14 / 0 评论 / 13 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年02月16日,已超过173天没有更新,若内容或图片失效,请留言反馈。
2015 年正式发布的 ES6 至今 7 年了,然而保留 ES5 习惯的人还是很多,好好看书好好学习
本文结合了阮一峰《ES6标准入门》第 2 版

let 和 const 命令

{ } 包裹起来的称为块语句,用于把语句组合起来,比如常见的 for 循环,如果不加 { } 只有一行代码会进入循环 (开端 粉丝狂喜 😯 ),加上了花括号后,多行代码会一起进入循环

💧 块语句是有块级作用域的,但 let 和 const 有块级作用域,var 声明的变量是没有的:

{{{ var a = 100 }}}f
a // 100

{let a = 100};
a // ReferenceError: a is not defined

💧 let 和 const 没有变量提升

a // undefined
b // ReferenceError: b is not defined
var a = 100;
let b = 200;

💧 let 和 const 的暂时性死区:在变量声明之前,任何对变量的引用都是非法的,变量不可用

{
  a = 100; // Cannot access 'a' before initialization
  let a;
}

💧 不允许重复声明,包括函数参数变量的声明,块内变量的声明等

function compute(arg) {
  let arg; // SyntaxError: Identifier 'arg' has already been declared
}

💧 块级作用域可替代 IIFE 立即执行函数,块内的细节对外隐藏

{
  // do something
}

(function () {
 // do something  
})();

💧 与顶层对象脱钩,var 声明的对象会被添加到 window 中,let 和 const 的不会

var a = 100;
this.a // 100
window.a // 100

let b = 200;
window.b // undefined
this.b // undefined

💧 块级作用域内声明函数
ES5 规定函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明,ES6引入了块级作用域,明确允许在块级作用域之中声明函数。emmm,具体的内容很多,不过我觉得不是特别重要,笔试面试应该考不到,而且浏览器并没有按照上面的规定实现,具体就看 图一图二

关于 const 命令

上面的内容对于 let 和 const 都是相同的,但是有些关于 const 的细节容易被忽视:

  • const 只是保证变量名指向的地址不变,但 不保证其数据不变 ,基础数据类型的变量是不变的,引用数据类型的变量是可变的。对于数组类型,其各个内部函数都不影响使用

    const a = 100;
    a = 200; // TypeError: Assignment to constant variable.
    
    const student = {};
    student.age = 20; // 正常
    
    const students = [];
    students.push({}); // 正常
  • const 声明的变量必须在声明时就赋值

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

关于变量声明

ES5 可以使用 var 和 function,ES6 可以使用 let 和 const,以及 import 和 class,加起来一共 6 种变量声明方式



数组解构赋值

let [a, b, c] = [1, 2, 3];
let [foo, [[bar], baz]] = [1, [[2], 3]];
let [ , , third] = ["foo", "bar", "baz"]; // third: "baz"
let [head, ...tail] = [1, 2, 3, 4];       // head: 1, tail: [2, 3, 4]
let [x, y, ...z] = ['a'];                 // x: 'a', y: undefined, z: []

上面的例子仅仅展示了等号右面是数组的情况,事实上,只要等号右面的对象具有 Iterator 接口,就可以用数组的形式被结构赋值,如:后面笔记里会提到的 Set,DOM 中的 HTMLCollection,NodeList 等,再举一个 Generator 的例子:

// 用 ES6 Generator 来计算斐波那契数列
function* fibs() {
  var a = 0;
  var b = 0;
  while(true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

解构赋值允许指定 默认值 ,当没有显式为变量赋值时,变量使用给定的默认值。对于下面的例子,因为 x 没有成功解构到任何有效值,因此 x 被赋予默认值 1。 注意 :当且仅当被解构的位置是 undefined 时,才会使用默认值,这里是 严格相等 的判断。

let [x = 1] = [];
let [y = 2] = [null];
x    // 1
y    // null 而不是 2, null 并不严格相等于 undefined

另外一个需要注意的地方是解构赋值的顺序,是先判断被解构的位置是否是 undefined,然后根据结果来决定要不要使用默认值。这意味着,如果默认值是一个表达式,这个表达式就是惰性的,只有用到的时候才会被调用求值:

function com() {
  console.log('called me');
  return 'OK';
}
let [x = com()] = [1];
// com 并不会被调用

还有一个特性是,在解构赋值表达式中,等号左边变量的默认值可以引用其他变量,前提是被引用的变量已经声明过了。emmm,我是觉得这个特性还是很绕的,影响可读性?反正我不用 😢

let [x = 1, y = x] = [];
// x: 1, y: 1
let [x = 1, y = x] = [2];
// x: 2, y: 2
let [x = y, y = 1] = [];
// ReferenceError: y is not defined



对象解构赋值

数组的结构赋值结果和 元素在数组中的 排列次序 有关,而对象的解构赋值结果是和变量名相关的,与次序无关。

let {name, age} = {age: 10, name: 'XiaoLu'};
// name: 'XiaoLu', age: 10
let {phone} = {age: 10, name: 'XiaoLu'};
// phone: undefined

💧 如果想要变量名不相等,如经常会出现前后端变量名命名格式不一致问题,前端习惯全部小写以下划线间隔,而调用某个 API 接口后返回的是驼峰式的命名,必须使用下面的写法:

let {studentName: student_name} = {studentName: 'xiaolu'};
// student_name: 'xiaolu'
// studentName: ReferenceError: studentName is not defined

💧 另外,对象解构赋值也支持嵌套形势。其实,坦白的说,能够想到的形式,应该都是支持的,比如对象解构赋值中再嵌套数组的解构赋值:

let {nums: [a] } = {nums: [1, 2 , 3]}
a // 1

💧 不过我觉得还是以 代码可读性 为首要原则,技巧再高超的代码让人看不懂也是不行的。比如书上为了说明给了这样的样例代码,一时半会儿是看不懂是把哪些值赋给了哪些变量。因为这里面 p 是模式,x 和 y 才是变量。

var obj = {
  p: [
    'Hello',
    {y: 'World'}
  ]
};
var {p: [x, {y}]} = obj;

💧 对象的解构可以指定默认值,主要是后两种不常见:

let {x = 3} = {} // x: 3
let {x, y = 5} = {x: 1} // x: 1, y: 5
let {x: y = 3} = {} // y: 3
let {x: y = 3} = {x: 5} // y: 5

0

评论 (0)

取消