??:空值合并运算符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 指令开头时才是严格的,它们不像函数声明或函数表达式那样“继承”当前上下文的严格性