1、Object.assign() 对象的合并2、链判断运算符(链式判断对象的某个属性)
3、Null 判断运算符(指定默认值)

1、Object.assign() 对象的合并

用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象中。

Object.assign(target, source1, source2) target:目标对象
source1,…sourcen:源对象
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

const target = {a: 1, b: 1}
const source1 = {b: 2, c: 2}
const source2 = {c: 3}

Object.assign(target, source1, source2)
target // {a: 1, b: 2, c: 3}

如果参数不是对象,则会先转成对象,然后返回。

console.log(Object.assign(2)) // Number {2}
typeof Object.assign(2) // object

由于 undefinednull 无法转成对象,所以如果他们作为参数会报错。

如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果undefined和null不在首参数,就不会报错。

let obj = {a: 1}
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

其他类型的值(即数值、字符串、布尔值)不在首参数也不会报错。但是除了字符串会以数组形式,拷入目标对象,其他值都不会有效果。

const v1 = 'abc'
const v2 = 10
const v3 = true
const obj = Object.assign({}, v1, v2, v3)
obj // {0: "a", 1: "b", 2: "c"}

实行的是浅拷贝

const obj1 = {a: {b: 1}}
const obj2 = Object.assign({}, obj1)

obj1.a.b = 2
obj2.a.b // 2

当想克隆到一个空对象时,只能克隆原始对象自身的值,不能克隆他继承的值。如果想要保持继承链时,可以采用以下代码。

function clone (origin) {
	let originProto = Object.getPrototypeOf(origin)
	return Object.assign(Object.create(originProto), origin);
}

ps:Object.create(obj) 创建的是原型,即

let p = Object.create(obj)
p.__proto__ === obj // true
let ab = Objct.assign({}, a, b)
// 等同于
let ab = {...a, ...b}

2、链判断运算符(链式判断对象的某个属性)

如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取 message.body.user.firstName, 安全的写法如下:

const firstName = (message
	&& message.body
	&& message.body.user
	&& message.body.user.firstName) || 'default'

需要判断四次,每一层是否有值。
三元运算符 ?:也常用于判断对象是否存在。

const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined

这样层层的判断很复杂,所以 ES2020 引入了“链判断运算符” ?.,来简化上面的写法

const frstName = message?.body?.user?.firstName || 'default'
const foolValue = myForm.querySelector('input[name=foo]')?.value

上面代码使用了?.运算符,直接在链式调用的时候判断,左侧的对象是否为nullundefined。如果是的,就不再往下运算,而是返回undefined

下面是?. 运算符常见形式,以及不使用该运算符时的等价形式。

a?.b
// 等同于
a === null ? undefined : a.b

a?.[x]
// 等同于
a === null ? undefined : a[x]

a?.b()
// 等同于
a === null ? undefined : a.b()

a?.()
// 等同于
a === null ? undefined : a()

使用这个运算符,有几个注意点。

(1)短路机制

?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行。

a?.[++x]
// 等同于
a === null ? undefined : a[++x]

如果 a 等于 null 或者 undefined,会直接返回undefined,而不执行 ++x。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。

(2)delete 运算符

delete a?.b
// 等同于
a === null ? undefined : delete a.b

如果 a 是 undefined 或者 null,会直接返回 undefined,而不会执行delete 运算。

(3)括号的影响

(a?.b).c
// 等价于
(a === null ? undefined : a.b).c

上面代码中,?.对圆括号外部没有影响,不管a对象是否存在,圆括号后面的.c总是会执行。

一般来说,使用?.运算符的场合,不应该使用圆括号。

3、Null 判断运算符(指定默认值)

只要属性的值为nullundefined,默认值就会生效,但是如果用 ||,属性的值如果为空字符串false0,默认值也会生效。
为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为nullundefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

上面代码中,默认值只有在左侧属性值为null或undefined时,才会生效。

这个运算符的一个目的,就是跟链判断运算符 ?. 配合使用,为null或undefined的值设置默认值。

const animationDuration = response.settings?.animationDuration ?? 300;

上面代码中,如果response.settings是null或undefined,或者response.settings.animationDuration是null或undefined,就会返回默认值300

?? 有一个运算优先级问题,它与&&||的优先级孰高孰低。如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。

(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);

(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);

(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);

(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);