ES-4 解构赋值、函数默认值、数组解构、对象解构
ES-5 隐式转换、函数参数解构、解构本质、()用法
一 解构赋值
1 虚值
- 含义:在Boolean转换结果为假的值
falsy
2 函数默认值
ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。(默认值如果是函数,当非undefined的情况,函数不会执行,只有在用到的时候,才会求值。)
// es5写法
function foo(x, y) {
// x = x || 1;
// y = y || 2;
// 以上写法,遇0出bug
// 计算类型的,要取得正确结果,要考虑0的情况,应如下
if (x !== 0) {
x = x || 1;
}
if (y !== 0) {
y = y || 1;
}
console.log(x + y);
}
以上计算,更人性化的写法应当是将null也转为0
// es6写法 能计算正确结果
function foo(x = 1, y = 2) {
console.log(x + y)
}
// 转译es5
"use strict";
function foo() {
var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
console.log(x + y);
}
// null的时候,涉及到隐式类型转化Number(null)为0
let x = 1;
function foo(x = 2) {
let x = 2; // 报错
// Identifier 'x' has already been declared.
console.log(x)
}
foo()
let x = 1;
function foo(y = x) {
console.log(y) // 1
}
foo()
不要和for循环的父子级作用域搞混了,函数形参和函数体属于同一作用域。
- 现象
function foo(x = x) {
console.log(x) // 无实参时报错,TDZ
}
foo()
let x = 1;
function foo(x = x) {
console.log(x) // 无实参时报错,2
}
foo(2)
// 不报错!
function bar(x = 2, y = x) {
return [x, y];
}
bar(); // [2, 2]
注意
var w = 1, z = 2;
function foo(x = w + 1, y = x + 1, z = z + 1) {
// 报错地方在z
// Uncaught ReferenceError: Cannot access 'z' before initialization
console.log(x, y, z)
}
foo()
- 形参默认值 - 惰性求值,不缓存,每次都重新计算
let a = 99;
function foo(b = a + 1) {
console.log(b)
}
foo() // 100
a = 100
foo() // 101
- 在项目中的应用
3 数组解构
- 模式匹配(结构化赋值)
- 解构失败时,结果为undefined
- 不完全解构:提供的值比需要解构的变量多
- 变量给默认值+解构
let [a = 6] = [1]
console.log(a) // 1
let [a = 6] = []
console.log(a) // 6
// 解构失败,模式不匹配
let [a = 6] = {} // 报错
// {} is not iterable
console.log(a)
let [a, b = 6] = [1, undefined]
console.log(a, b) // 1 6
let [a, b = 6] = [1, null]
console.log(a, b) // 1 null
let [a = 1, b = a] = []
console.log(a, b) // 1 1
let [a = 1, b = a] = [2]
console.log(a, b) // 2 2
4 对象解构
let firstName = 'Jessica';
let lastName = 'Jung';
let superIdol = {
[firstName + lastName]: 'owner of e&b'
}
console.log(superIdol)
-
解构语句的语法报错:认为等号左边是语法块
-
加
()
变成表达式
-
声明变量时不要乱加括号
[(b)] = [3];
console.log(b); // 3
({a:(b) = {}}) // 本身没有进行匹配,而是默认值
console.log(b); // {}
- 变量值互换
let a = 1, b = 100;
[a, b] = [b, a]
console.log(a, b) // 100 1
5. 解构本质
事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
- 变量的解构就是变量的赋值
- 模式匹配可以匹配同源属性
var x = 200, y = 300, z = 100;
var obj1 = {
x: {
y: 42
},
z: {
y: z
}
};
({ y: x = { y: y } } = obj1);// x = {y: y} → x = {y: 300}
({ z: y = { y: z } } = obj1);// y = {y: z} → y = {y: 100}
({ x: z = { y: x } } = obj1);// z = {y: 42}
console.log(x.y, y.y, z.y) // 300 100 42
function test([x, y]) { // 报错
// Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
console.log(x, y)
}
test()
function foo({ x = 10 } = {}, { y } = { y: 10 }) {
console.log(x, y)
}
foo() // 10 10
foo({}, {}) // 10 undefined
foo({x: 2}, {y: 3}) // 2 3
注意
({ x = 10 } = {});
// ({ x: x = 10 } = {}); 以上是属性和变量相同时的es6写法
({ y } = { y: 10 });
// ({ y: y } = { y: 10 });
console.log(x, y); // 10 10
6 解构的隐式转换
- 字符串隐式转换类数组
const [a, b, c, d, e] = 'hello'
console.log(a, b, c, d, e) // h e l l o
let {length: len} = 'hello'
console.log(len) // 5
- 数字类型/布尔值隐式转换为包装类
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
7. 对函数length属性的影响
- 当函数形参给默认值时,从当前位置及之后都不计入length(形参个数)的计算
- 也就是说,指定了默认值后,length属性将失真。
- 这是因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。
function test(a, b, c = 0) {
}
console.log(test.length) // 2
- 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
function test(c = 0, a, b) {
}
console.log(test.length) // 0
- 形参实参的映射关系不再成立
8. 函数默认值与作用域
相当复杂的一系列例子
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。