??
:空值合并运算符es2020
??
是一个逻辑操作符,当左侧的操作数为null
或者undefined
时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或操作符||
不同,逻辑或操作符会在左侧操作数为假值
时返回右侧操作数。也就是说,如果使用 ||
来为某些变量设置默认值,可能会遇到意料之外的行为。
比如为假值(例如,''
或 0
)时。见下面的例子:
const foo = null ?? 'default string'
console.log(foo) // "default string"
const baz = 0 ?? 42
console.log(baz) // 0
const bar = false ?? true
console.log(bar) // false
注意
:??
不能与 &&
或 ||
操作符共用
null || undefined ?? "foo" // 抛出 SyntaxError
true || undefined ?? "foo" // 抛出 SyntaxError
但是
,如果使用括号来显式表明运算优先级,是没有问题的:
(null || undefined ) ?? "foo" // 返回 "foo"
?.
:可选链操作符es2020
可选链操作符
?.
允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
?.
操作符的功能类似于.
链式操作符,不同之处在于,在引用为空值要么是null
要么是undefined
的情况下不会引起错误,该表达式短路返回值是undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回undefined
。
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
}
const dogName = adventurer.dog?.name
console.log(dogName) // undefined
console.log(adventurer.someNonExistentMethod?.()) // undefined
console.log(adventurer.list?.forEach(() => {})) // undefined
对比一下代码
const obj = {}
let nestedProp = obj.first?.second
这等价于以下表达式,但实际上没有创建临时变量:
const obj = {}
let temp = obj.first
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second)
可选链与函数调用
当尝试调用一个可能不存在的方法时也可以使用可选链。
- 函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回
undefined
而不是抛出一个TypeError
。
const someInterface = {}
let result = someInterface.customMethod?.()
可选链和表达式
当使用方括号与属性名的形式来访问属性时,你也可以使用可选链操作符:
let list = null
console.log(list?.[1]) // undefined
注意
- 如果存在一个属性名且不是函数, 使用
?.
仍然会产生一个TypeError
异常
const someInterface = { customMethod: 'Not Method' } let result = someInterface.customMethod?.(); // Uncaught TypeError: someInterface.customMethod is not a function
- 如果
someInterface
自身是null
或者undefined
, 仍然会产生一个TypeError
异常,如果你希望允许someInterface
也为null
或者undefined
,那么你需要像这样写:
let someInterface = null someInterface?.customMethod?.() // undefined
逗号运算符间接函数调用
先介绍一下逗号运算符
逗号运算符
对它的每个操作数求值(从左到右),并返回最后一个操作数的值。
let x = 1
x = (x++, x)
console.log(x) // 2
x = (2, 3)
console.log(x) // 3
(0, alert)('ByePast')
逗号运算符衍生出来的间接函数调用
使用间接函数调用可以把该作用域内倍的
this
指向全局对象中的this
在调用函数的上下文中,对操作数的评估只会得到一个值,而不是引用,这会导致this
被调用函数内部的值指向全局对象(或者它将undefined
处于新的 ECMAScript 5 严格模式
)例如:
var foo = 'global.foo'
var obj = {
foo: 'obj.foo',
method: function () {
return this.foo
}
};
obj.method() // 'obj.foo'
(1, obj.method)() // 'global.foo'
如您所见,第一次调用,即直接调用,this
内部的值method
将正确引用obj
(返回'obj.foo'
),第二次调用,逗号运算符所做的评估将使this
值指向全局对象
(产生'global.foo'
)。
这种模式很流行,要间接调用eval
,这在 ES5 严格模式
下很有用,例如,获取对全局对象的引用(假设您在非浏览器环境中,window
不是可用的)
(function () {
'use strict';
var global = (function () { return this || (1, eval)('this') })();
})();
在上面的代码中,内部匿名函数将在严格模式代码单元内执行,这将导致this
值为undefined
该||
运营商现在就第二个操作数的eval
调用,这是一个间接调用,它将评估对全球词法和环境变量的代码。
但就我个人而言,在这种情况下,在严格模式下,我更喜欢使用Function
构造函数来获取全局对象:
(function () {
'use strict'
var global = Function('return this')()
})()
使用Function
构造函数创建的函数只有在以 use strict
指令开头时才是严格的,它们不像函数声明或函数表达式那样“继承”当前上下文的严格性