今天看到有人提出这样一个问题,下面是代码:
1 | var ob1 = new Boolean(false); |
很奇怪是吧,继续看第二个例子:
1 | var a = 2; |
问 andflag 和 orflag 的值各是多少。乍看,认为 andflag 和 orflag 的值均为 true。因为 && 和 || 都是求 Boolean,众所周知,JavaScript 是弱类型的语言,会进行类型转换。
但答案是 andflag 为 3, orflag 为 2。
经过和朋友的讨论,查阅书籍,终于知道了原因,首先要说一下 && 运算,在《JavaScript高级程序设计》中有这样的描述:
逻辑与操作符可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作符就不一定返回布尔值;此时,它遵循下列规则:
- 如果第一个操作数是对象,则返回第二个操作数;
- 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象;
- 如果两个操作数都是对象,则返回第二个操作数;
- 如果有一个操作数是 null/NaN/undefined,则返回 null/NaN/undefined。
在第一个例子中,ob1 是用 Boolean 构造函数创建的值为 false 的 Boolean 对象,所以依据上面的规则,第二行第一个操作数为对象,则返回 true。第三行第二个操作数是对象,第一个操作数为 true,所以结果返回该 Boolean 对象。
另外,这里的 ob3 为一个对象,alert() 会自动调用 toString() 方法,若要 console.log() 与 alert() 一致,则应显式调用 toString() 方法。
相比JS红皮书中的描述,犀牛书中的描述则更为明了,本质是相同的:
&& 的操作数并不一定是布尔值,对其它类型的值(假值为 false、null、undefined、0、-0、NaN 和 “”,所有其它的值都是真值)也可因进行布尔操作。如果两个数都是真值,那么返回一个真值;否则,如果至少一个操作数是假值,则返回一个假值。进一步讲,运算符首先计算 && 左侧的表达式。如果计算结果为假值,则 && 返回左侧表达式的值,而不会对右操作数进行计算。如果左操作数是真值,&& 运算符将右操作数的值并将其返回作为整个表达式的运算结果。
由此,第二个例子的答案就显而易见了。同理,对于逻辑或操作符,它首先计算第一个操作数的值,如果结果为真,则返回左操作数,否则计算第二个操作数,并返回第二个操作数。
逻辑与操作与逻辑或操作均属于短路操作。当 && 右侧的表达式具有副作用(赋值、递增、递减和函数调用表达式)时,它们的执行依赖左操作数的计算结果。
下面的 JavaScript 代码是等价的:
1 | if (a == b) foo(); |
而 || 最常用此特性的方式是为变量提供后备值或在函数中为参数提供默认值:
1 | var myValue = preferredValue || backupValue; |
将第一个例子改写下,得到第三个例子:
可以看到例一的运行结果与最初的期望一致了,这是因为在这里ob1不是一个对象,而是 boolean 值。使用 new 调用基本类型的构造函数,与直接调用同名的转型函数是不同的。下面是基本类型的布尔值与引用类型的布尔值的区别:
1 | var trueValue = true; |
通过以上讨论可以很自然的理解大多数 JavaScript 书籍和教程中指出的一点:
我们的建议是永远不要使用 Boolean 对象。
END
参考:
- 《JavaScript高级程序设计》
- 《JavaScript权威指南》
- js中的逻辑与(&&)和逻辑或(||)