文章目录
变量的解构赋值
数组的解构赋值
基本用法:
let [a, b, c] = [1, 2, 3];
console.log({a, b, c}); // { a: 1, b: 2, c: 3}
上面代码表示,可以从数组中提取值,按照对应的位置,对变量进行赋值。
如果解构不成功,变量的值就等于undefined。
let [foo] = [];
let [bar, foo] = [1];
以上两种情况都属于解构不成功,foo的值都会等于undefined。
在进行解构时,也可以只解构一部分数据。
let [x, y] = [1, 2, 3];
console.log({x, y}); // { x: 1, y: 2 }
这种情况是属于不完全解构
默认值:
解构赋值允许指定默认值。
let [x= 1] = [];
x // 1
let [x, y] = [1];
{x, y} // { x: 1, y: undefined }
let [x, y = 2] = [1, undefined];
{x, y} // { x: 1, y: 2 }
在 ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x = 1] = [undefined]
x // 1
let [x = 1] = [null]
x // null
对象的解构赋值
基本用法:
let { x, y } = { x: 'a', y: 'b' };
x // 'a'
y // 'b'
let { y, x, z } = { x: 'a', y: 'b' }
x // 'a'
y // 'b'
z // undefined
变量必须与对象的属性同名,才能取到正确的值,否则undefined。
对象的解构赋值是下面形式的简写。
let { x: x, y: y } = { x: 'a', y: 'b' };
也就是说,对象的解构赋值的内部机制,是先找到同名的属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { x: name } = { x: 'a' }
name // 'a'
x // error: x is not defined
默认值:
let { x = 3 } = {}
x // 3
默认值生效的条件是,对象的属性值严格等于undefined。
let { x = 3 } = { x: undefined }
x // 3
let { x = 3 } = { x: null };
x // null
字符串的解构赋值
字符串在进行解构赋值时会被转换成一个类似数组的对象。
let [x, y, z] = 'age'
x // a
y // g
z // e
let { length: len } = 'age'
len // 3
函数参数的解构赋值
function log([x, y]) {
console.log({ x, y }); // { x: 1, y: 2 }
}
log([1, 2]);
函数参数的解构赋值也可以使用默认值:
function log({ x = 0, y = 0 } = {}) {
console.log({ x, y });
}
log({ x: 1, y: 2 }); // { x: 1, y: 2 }
log({ x: 1 }); // { x: 1, y: 0 }
log({}); // { x: 0, y: 0 }
log(); // { x: 0, y: 0 }
用途
交换变量的值:
let x = 1,
y = 2;
[x, y] = [y, x];
console.log({ x, y }); // { x: 2, y: 1 }
从函数返回多个值:
function log1() {
// 返回一个数组
return [1, 2, 3];
}
function log2() {
// 返回一个对象
return { name: "Tom", age: 100 };
}
let [a, b, c] = log1();
console.log({ a, b, c }); // { a: 1, b: 2, c: 3 }
let { name, age } = log2();
console.log({ name, age }); // { name: "Tom", age: 100 }
函数参数的定义:
// 参数是一组有次序的值
function log([name, age, color]) {
console.log({ name, age, color }); // { name: 'Tom', age: 18, color: 'pink' }
}
log(["Tom", 18, "pink"]);
// 参数是一组无次序的值
function log({ name, age, color }) {
console.log({ name, age, color }); // { name: 'Tom', age: 18, color: 'pink' }
}
log({ age: 18, color: "pink", name: "Tom" });
字符串的扩展
遍历器接口:
for...of
为字符串添加了遍历器接口,使得字符串可以被for...of
循环遍历。
for (let key of "name") {
console.log(key); // n a m e
}
模板字符串
模板字符串是增强版的字符串,用反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
// 普通模板字符串
const str1 = `hello world`;
// 多行字符串
const str2 = `hello
world
`;
// 字符串中签入变量
const name = "Tom";
const str3 = `名字:${name}`;
console.log(str1);
console.log(str2);
console.log(str3);
函数的扩展
参数的默认值
// ES5 写法
function log(x, y) {
if (typeof y === 'undefined') {
y = 100;
}
console.log(x, y);
}
log(10); // 10 100
log(10, 20); // 10 20
// ES6 写法
function log(x, y = 100) {
console.log(x, y);
}
log(10); // 10 100
log(10, 20); // 10 20
与解构赋值默认值结合使用:
function log({ x, y = 100 }) {
console.log(x, y);
}
log({ x: 10 }); // 10 100
log({ x: 10, y: 20 }); // 10 20
log(); // Cannot destructure property 'x' of 'undefined' as it is undefined.
// 无法对“undefined”的属性“x”进行分解,因为它是未定义的。
上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数log的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数log调用时没有提供参数,变量x和y就不会生成,从而报错。解决方法如下:提供函数参数的默认值
function log({ x, y = 100 } = {}) {
console.log(x, y);
}
log(); // undefined 100
上面代码指定,如果没有提供参数,函数log的参数默认为一个空对象。
下面两种写法有什么差别:
// 写法一
function log1({ x = 0, y = 0 } = {}) {
console.log([x, y]);
}
function log2({ x, y } = { x: 0, y: 1 }) {
console.log([x, y]);
}
区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个具体属性的对象,但是没有设置对象解构赋值的默认值。
rest 参数
ES6 引入rest 参数
(...变量名
),用于获取函数的多余参数。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function log(...par) {
console.log(par);
}
log(1, 2, 3); // [1, 2, 3]
注意,rest参数之后不能再有其它参数(即只能是最后一个参数),否则会报错。
function log(...par, name) {
console.log(par);
}
log(1, 2, 3); // Rest parameter must be last formal parameter
// Rest参数必须是最后一个形式参数
对象的扩展
属性的简洁表示法
const person {
name,
age
}
// 等同于
const person {
name: name,
age: age
}
除了属性简写,方法也可以简写:
const person = {
getName() {
return "Tom"
}
}
// 等同于
const person = {
getName: function() {
return "Tom"
}
}
这种简洁写法在打印对象时也很有用:
const person1 = {
name: "Tom",
};
const person2 = {
name: "Jerry",
};
console.log(person1, person2); // {name: "Tom"} {name: "Jerry"}
console.log({ person1, person2 }); // {person1:{name: "Tom"}, person2: {name: "Jerry"}}
上面代码中,把两个对象放在大括号里面输出,就变成了对象的简洁表示法,每组键值对前面会打印对象名,这样就比较清晰了。
注意,简写的对象方法不能用作构造函数,会报错:
const obj = {
person1: function () {
this.name = "Tom";
},
person2() {
this.name = "Jerry";
},
};
const person1 = new obj.person1();
console.log(person1); // { name: "Tom" }
const person2 = new obj.person2(); // obj.person2 is not a constructor
属性名表达式
在 ES5 中,对象字面量定义对象时,只能使用标识符作为属性名:
const person = {
name: "Tom"
}
但在 ES6 中,可以使用表达式作为对象的属性名,即把表达式放在方括号内:
const obj = {
["na" + 'me']: "Tom"
}
// 等同于
const obj = {
name: "Tom"
}
super 关键字
在 ES6 中新增了super
关键字,指向当前对象的原型对象。
const animal = {
type: "animal",
};
const cat = {
type: "cat",
getType() {
console.log(super.type);
// 等同于
// Object.getPrototypeOf(this).foo
},
};
Object.setPrototypeOf(cat, animal);
cat.getType(); // animal
上面代码中,对象cat.getType()
方法之中,通过super.type
引用了原型对象animal
的type
属性。
注意:super
关键字表示原型对象时,只能用在对象的方法之中,用在其它地方都会报错:
const animal = {
type: "animal",
};
const cat = {
type: super.type, // 'super' keyword unexpected here
_getType: () => super.type, // 'super' keyword unexpected here
getType: function () {
return super.type; // 'super' keyword unexpected here
},
};
Object.setPrototypeOf(cat, animal);
上面三种super
的用法都会报错,因为对于 JS 引擎来说,这里的super
都没有用在对象的方法之中。第一种写法是super
用在属性里面,第二种和第三种写法是super
用在一个函数里面,然后赋值给_getType/getType
属性。目前,只有对象方法的简写法可以让 JS 引擎确认,定义的是对象的方法。
在 JS 引擎内部,super.xxx
有下面两种情况:
super.属性
// 等同于
Object.getPrototypeOf(this).type
super.方法
// 等同于
Object.getPrototypeOf(this).getType.call(this)
const animal = {
type: "animal",
_getType() {
return this.type;
},
};
const cat = {
type: "cat",
getType() {
return super._getType(); // Object.getPrototype(this)._getType.call(this)
},
};
Object.setPrototypeOf(cat, animal);
console.log(cat.getType()); // 'cat'
上面代码中,super._getType
指向原型对象animal
的_getType
方法,但是绑定的this
却还是当前对象cat
,因此输出的是cat
。