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,如果参数类型不是NaNNumber.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 判断运算符??,只有运算符左侧的值为nullundefined时,才会返回右侧的值。这个运算符的一个目的,就是跟链判断运算符?.配合使用,为nullundefined的值设置默认值。

js 复制代码
const animationDuration = response.settings?.animationDuration ?? 300;

如果response.settingsnullundefined,或者response.settings.animationDurationnullundefined,就会返回默认值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 语法你在工作中用的多吗?这同时也是面试中必考的内容,所以我们在工作中还是要多学多用,多学多记,熟能生巧。

相关推荐
程序员凡尘20 分钟前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
北岛寒沫5 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
everyStudy5 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
无心使然云中漫步6 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者6 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋8 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120538 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢8 小时前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写9 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js