ES6快速入门(上)

概述

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 语法你在工作中用的多吗?这同时也是面试中必考的内容,所以我们在工作中还是要多学多用,多学多记,熟能生巧。

相关推荐
码艺-Alimjan20 分钟前
维吾尔语 RTL 排版的隐形利器:`\u200F`
前端·javascript
Highcharts.js1 小时前
Highcharts 数据过滤实战指南:从基础实现到性能优化
javascript·性能优化·bi·highcharts·前端可视化·数据过滤·交互图表
何何____2 小时前
TypeScript 函数类型兼容性
前端·javascript·typescript
雪芽蓝域zzs2 小时前
json数据转化成interface属性
前端·javascript·json
鬼手点金3 小时前
ReAct 循环
linux·服务器·前端·javascript·react.js·前端框架
雪芽蓝域zzs3 小时前
Vue导入方法三种方式对比
前端·javascript·vue.js
VimorphTech5 小时前
React 任务轮询,为什么不能只写一个 setInterval?
javascript·typescript
lexiangqicheng5 小时前
Docker的沙箱机制是怎样的?
前端·javascript
LCG米6 小时前
STM32 HardFault故障定位实测:压栈帧寄存器解析、CFSR故障源判读与出错地址回溯
前端·javascript·stm32·单片机·嵌入式