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
由于 undefined
和 null
无法转成对象,所以如果他们作为参数会报错。
如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果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
上面代码使用了?.
运算符,直接在链式调用的时候判断,左侧的对象是否为null
或undefined
。如果是的,就不再往下运算,而是返回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 判断运算符(指定默认值)
只要属性的值为null
或undefined
,默认值就会生效,但是如果用 ||
,属性的值如果为空字符串
或false
或0
,默认值也会生效。
为了避免这种情况,ES2020 引入了一个新的 Null
判断运算符??
。它的行为类似||
,但是只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。
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);