概述
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。ES6 这个词的原意,就是指 JavaScript 语言的下一个版本,其标准在每年的 6 月份正式发布一次。
ECMAScript 2024 草案版本已经在其官网发布,草案的发布意味着TC39在今年6月发布正式版时与草案版内容不会相差太大,可能会在其基础上做小幅度的修正。
而 ES6 的第一个版本,在 2015 年 6 月发布,正式名称就是《ECMAScript 2015 标准》(简称 ES2015),是内容更新篇幅最大的一次,也是我们值得深入学习 JavaScript 特性的一个版本。
本文内容总结于阮一峰老师的《ECMAScript 6 入门教程》,列举了我们在平时常用的ES6相关语法,更多详细的内容可查阅阮一峰老师的这本开源书籍。
由于内容篇幅较大,我们分三个小节进行学习。
let 和 const 命令
let
声明的变量只在它所在的代码块有效。
const
声明一个只读的常量。一旦声明,常量的值就不能改变,只声明不赋值,就会报错。
不存在变量提升
用var
声明变量时,会发生变量提升,当脚本执行的时候,变量就已经存在,此时该变量没有被赋值。
js
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
暂时性死区
在代码块内,使用let
命令或者const
命令声明变量之前,变量都是不可用的,变量一定要在声明之后使用,否则就报错。
js
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。
js
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
块级作用域
外层代码块不受内层代码块的影响。
js
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
块级作用域之中,函数声明语句的行为类似于let
,在块级作用域之外不可引用。
变量的解构赋值
数组的解构赋值
可以从数组中提取值,按照对应位置,对变量赋值。
js
let [a, b, c] = [1, 2, 3];
只要等号两边的模式相同,左边的变量就会被赋予对应的值。
js
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
只要某种数据结构具有 Iterator
接口,都可以采用数组形式的解构赋值。
js
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
解构赋值允许指定默认值。
js
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
只有当一个数组成员严格等于undefined
,默认值才会生效。
js
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
对象的解构赋值
js
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
如果变量名与属性名不一致,必须写成下面这样。
js
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
对象的解构也可以指定默认值。
js
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
默认值生效的条件是,对象的属性值严格等于undefined
。
js
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
字符串的解构赋值
字符串也可以解构赋值。字符串被转换成了一个类似数组的对象。
js
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象,解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
js
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
函数参数的解构赋值
js
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
函数参数的解构也可以使用默认值。
js
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
字符串的扩展
字符串的遍历器接口
字符串可以被for...of
循环遍历。
js
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
js
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
标签模板
标签模板其实不是模板,而是函数调用的一种特殊形式。标签指的就是函数,紧跟在后面的模板字符串就是它的参数。
js
alert`hello`
// 等同于
alert(['hello'])
模板字符里面的变量,会将模板字符串先处理成多个参数,再调用函数。
js
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
其中tag
表示一个函数,整个表达式的返回值,就是tag
函数处理模板字符串后的返回值。
而tag
函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,也就是除去${...}
这部分内容,即'Hello'
、'world'
、' '
,tag
函数的其他参数,都是模板字符串各个变量被替换后的值,即${a + b}
、${a * b}
的值。
字符串的新增方法
includes()、startsWith()、endsWith()
- includes() :返回布尔值,表示是否找到了参数字符串。
- startsWith() :返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith() :返回布尔值,表示参数字符串是否在原字符串的尾部。
js
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
repeat()
repeat
方法返回一个新字符串,表示将原字符串重复n
次。
js
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
padStart(),padEnd()
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
js
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
trimStart(),trimEnd()
ES2019 对字符串实例新增了trimStart()
和trimEnd()
这两个方法。trimStart()
消除字符串头部的空格,trimEnd()
消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
js
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
浏览器还部署了额外的两个方法,trimLeft()
是trimStart()
的别名,trimRight()
是trimEnd()
的别名。
replaceAll()
ES2021 引入了replaceAll()
方法,可以一次性替换所有匹配。
js
'aabbcc'.replaceAll('b', '_')
// 'aa__cc'
at()
at()
方法接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)。
js
const str = 'hello';
str.at(1) // "e"
str.at(-1) // "o"
数值的扩展
Number.isFinite(), Number.isNaN()
Number.isFinite()
用来检查一个数值是否为有限的(finite),即不是Infinity
,如果参数类型不是数值,Number.isFinite
一律返回false
。
js
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
Number.isNaN()
用来检查一个值是否为NaN
,如果参数类型不是NaN
,Number.isNaN
一律返回false
。
js
Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true' / 0) // true
Number.isNaN('true' / 'true') // true
Number.isFinite()
对于非数值一律返回false
, Number.isNaN()
只有对于NaN
才返回true
,非NaN
一律返回false
。
Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()
和parseFloat()
,移植到Number
对象上面,行为完全保持不变。
js
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger()
Number.isInteger()
用来判断一个数值是否为整数。
js
Number.isInteger(25) // true
Number.isInteger(25.1) // false
数组的扩展
扩展运算符
扩展运算符(spread)是三个点(...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
js
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
扩展运算符提供了复制数组的简便写法。
js
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
扩展运算符提供了数组合并的新写法。
js
onst arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
扩展运算符可以与解构赋值结合起来,用于生成数组。
js
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
扩展运算符还可以将字符串转为真正的数组。
js
[...'hello']
// [ "h", "e", "l", "l", "o" ]
Array.from()
Array.from()
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
js
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6 的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.of()
Array.of()
方法用于将一组值,转换为数组。
js
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
主要是弥补数组构造函数Array()
的不足。
js
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
Array.of()
基本上可以用来替代Array()
或new Array()
,并且不存在由于参数不同而导致的重载。总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
find(),findIndex(),findLast(),findLastIndex()
数组实例的find()
方法,用于找出第一个符合条件的数组成员。
js
[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
数组实例的findIndex()
方法的用法与find()
方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
。
js
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
find()
和findIndex()
都是从数组的0号位,依次向后检查。ES2022 新增了两个方法findLast()
和findLastIndex()
,从数组的最后一个成员开始,依次向前检查,其他都保持不变。
js
const array = [
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2
fill()
fill
方法使用给定值,填充一个数组。
js
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
js
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
entries(),keys() 和 values()
ES6 提供三个新的方法------entries()
,keys()
和values()
------用于遍历数组。它们都返回一个遍历器对象,可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
js
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
includes()
ES2016 引入了Array.prototype.includes
,该方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。
js
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。
js
if (arr.indexOf(el) !== -1) {
// ...
}
flat(),flatMap()
Array.prototype.flat()
用于将嵌套的数组"拉平",变成一维的数组。该方法返回一个新数组,对原数据没有影响。
js
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
flat()
默认只会"拉平"一层,如果想要"拉平"多层的嵌套数组,可以将flat()
方法的参数写成一个整数,表示想要拉平的层数,默认为1。
js
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
at()
ES2022 为数组实例增加了at()
方法,接受一个整数作为参数,返回对应位置的成员,并支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组。
js
const arr = [5, 12, 8, 130, 44];
arr.at(2) // 8
arr.at(-2) // 130
对象的扩展
属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
js
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
除了属性简写,方法也可以简写。
js
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
属性名表达式
ES6 允许字面量定义对象时,用方法表达式作为对象的属性名,即把表达式放在方括号内。
js
let lastWord = 'last word';
const a = {
'first word': 'hello',
[lastWord]: 'world'
};
a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
方法的 name 属性
函数的name
属性,返回函数名。对象方法也是函数,因此也有name
属性。
js
onst person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
对象的扩展运算符
解构赋值
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面,解构赋值必须是最后一个参数,否则会报错。
js
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
扩展运算符
对象的扩展运算符(...
)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
js
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
扩展运算符可以用于合并两个对象。
js
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
对象的新增方法
Object.is()
Object.is()
用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。区别:
+0
不等于-0
NaN
等于自身
js
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.assign()
Object.assign
将源对象(source)的所有可枚举属性,复制到目标对象(target)。
js
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。
js
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
Object.assign()
拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false
)。
js
Object.assign({b: 'c'},
Object.defineProperty({}, 'invisible', {
enumerable: false,
value: 'hello'
})
)
// { b: 'c' }
Object.assign()
方法实行的是浅拷贝,而不是深拷贝。如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用,这个对象的任何变化,都会反映到目标对象上面。
js
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
Object.assign()
可以用来处理数组,但是会把数组视为对象。
js
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
Object.getOwnPropertyDescriptors()
ES2017 引入了Object.getOwnPropertyDescriptors()
方法,返回指定对象所有自身属性(非继承属性)的描述对象。
js
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
__proto__属性
__proto__
属性(前后各两个下划线),用来读取或设置当前对象的原型对象(prototype)。
js
// es5 的写法
const obj = {
method: function() { ... }
};
obj.__proto__ = someOtherObj;
// es6 的写法
var obj = Object.create(someOtherObj);
obj.method = function() { ... };
Object.setPrototypeOf()
Object.setPrototypeOf()
方法的作用与__proto__
相同,用来设置一个对象的原型对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
js
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
Object.getPrototypeOf()
该方法与Object.setPrototypeOf()
方法配套,用于读取一个对象的原型对象。
js
function Rectangle() {
// ...
}
const rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype
// true
Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype
// false
Object.values()
Object.values()
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。
js
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
Object.entries()
Object.entries()
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。
js
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
Object.entries
的基本用途是遍历对象的属性。
js
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
console.log(
`${JSON.stringify(k)}: ${JSON.stringify(v)}`
);
}
// "one": 1
// "two": 2
Object.entries
方法的另一个用处是,将对象转为真正的Map
结构。
js
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }
Object.fromEntries()
Object.fromEntries()
方法是Object.entries()
的逆操作,用于将一个键值对数组转为对象。
css
Object.fromEntries([ ['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。
js
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }
Object.hasOwn()
ES2022 在Object
对象上面新增了一个静态方法Object.hasOwn(),可以判断是否为自身的属性。
js
const foo = Object.create({ a: 123 });
foo.b = 456;
Object.hasOwn(foo, 'a') // false
Object.hasOwn(foo, 'b') // true
对象foo
的属性a
是继承属性,属性b
是原生属性。
但是Object.hasOwn()
与obj.hasOwnProperty()
的区别在于,Object.hasOwn()
可以处理obj.hasOwnProperty()
对于不继承Object.prototype
的对象而导致报错的问题。
js
const obj = Object.create(null);
obj.hasOwnProperty('foo') // 报错
Object.hasOwn(obj, 'foo') // false
运算符的扩展
指数运算符(**)
js
2 ** 2 // 4
2 ** 3 // 8
多个指数运算符连用时,是从最右边开始计算的。
js
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
指数运算符可以与等号结合,形成一个新的赋值运算符(**=
)。
js
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
链判断运算符
js
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
这行代码必须先判断message
中的每一层属性是否存在,才能读取firstName
的值,ES2020 引入了"链判断运算符"?.
,用来简化这种写法,即:
js
const firstName = message?.body?.user?.firstName || 'default';
如果左侧对象为null
或者undefined
,就不再往下运算,也就是不再执行?.
后面的部分,而是返回undefined
。
Null 判断运算符
ES2020 引入了一个新的 Null 判断运算符??
,只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。这个运算符的一个目的,就是跟链判断运算符?.配合使用,为null
或undefined
的值设置默认值。
js
const animationDuration = response.settings?.animationDuration ?? 300;
如果response.settings
是null
或undefined
,或者response.settings.animationDuration
是null
或undefined
,就会返回默认值300。
逻辑赋值运算符
ES2021 引入了三个新的逻辑赋值运算符,将逻辑运算符与赋值运算符进行结合。
js
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
这三个运算符||=
、&&=
、??=
相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。
js
// 老的写法
user.id = user.id || 1;
// 新的写法
user.id ||= 1;
总结
ES6 自发布之日,至今已经有近10年的时间了,为什么说ES6尤为重要,因为这一版本引入了许多新的特性,使用其语法不仅能让我们提高开发效率,更有利于我们读懂一些大型的项目或者开源框架的源码,书写通俗易懂、优雅的代码。
ES6 之后每年都会发布新的标准,基本都是小幅度增加新的语法规范。以上这些 ES6 语法你在工作中用的多吗?这同时也是面试中必考的内容,所以我们在工作中还是要多学多用,多学多记,熟能生巧。