a = a || function(){...}
是Javascript中非常常见的习语。它依赖于两个概念,虽然不是Javascript独有的,但你可能还不熟悉。
1. 运算符短路
运算符短路[维基百科]是一种编译器优化,旨在防止不必要的计算。
为了证明这一点,假设我们想确定一个人是否是青少年:也就是说,一个人的年龄在13岁到19岁之间。
var isTeenager = person.age >= 13 && person.age <= 19
现在,假设代码执行后发现这个人年龄小于13岁。第一个条件将被评估并返回false。由于程序现在知道
&&
操作符左侧为
false
,并且
&&
要求两侧都为
true
才能评估为
true
,因此它知道评估右侧是毫无意义的。
换句话说,程序已经知道这个人的年龄不大于13岁,已经知道他不是青少年,不管他是否小于19岁都无所谓。
同样的原理也适用于
||
运算符。假设我们想知道一个人是否可以免费乘坐公交车:即该人是否超过70岁或残疾。
var canRideFree = person.age >= 70 || isHandicapped(person)
如果这个人超过70岁,程序已经知道他可以免费乘车。此时,程序不关心他是否残疾,因此不评估对
isHandicapped
函数的调用。另一方面,如果这个人年龄小于70岁,那么
canRideFree
将被设置为
isHandicapped
返回的任何值。
2.
真值和假值[some random person's blog]是对象的布尔运算结果。在Javascript中,每个对象都将计算出"真值"或"假值"。
如果表达式的值是以下任何一种情况,则表达式为"假值":
false, null, undefined, 0, "", NaN
其他所有值都是真值。
人们利用 null 或 undefined 变量的值为 false
这个事实。这意味着你可以很容易地检查变量是否存在:
if (a) { /* a exists and is not a falsy value */ }
将我们所知道的结合起来
||
运算符会短路并返回它评估的最后一个表达式的值。这个原则与真值性相结合,体现在以下单个语句中:
Object.keys = Object.keys || function() {...}
如果
Object.keys
为真值,它将被评估并分配给自身。否则,
Object.keys
将被分配给该函数。这是Javascript中非常常见的习语,用于检查一个值是否已经存在,如果不存在,则将其分配给其他内容。
一些其他的语言,如C#,没有真值,它们有一个类似的
null-coalescing operator[MSDN],具有类似的目的。
object Value = PossiblyNullValue ?? ValueIfNull
在这段代码中,如果
Value
不为null,则将其赋值给
PossiblyNullValue
,否则将其赋值给
ValueIfNull
。
如果你没有仔细阅读我上面说的任何内容,你只需要知道a = a || function() {...}
基本上做了这段代码所做的事情:
if (exists(Object.keys)) {
Object.keys = Object.keys;
} else {
Object.keys = function() {...};
}
function exists(obj) {
return typeof obj !== "undefined" &&
obj !== null &&
obj !== false &&
obj !== 0 &&
obj !== "" &&
!isNaN(obj);
}
a||(a=b)
而不是a=a||b
,它们看起来很相似且做的事情一样,但前者不会在a已经为truthy时重新分配给自己,这样稍微快一些。参见http://jsperf.com/a-a-b(我正在手机上编写和运行测试,因此仅在Android上进行了基准测试-但我认为您应该在其他浏览器中看到类似的结果,除非它们通过某种方式优化并知道无需运行`a=a`)。 - shesek