javascript 无语的==

今天面试不当心掉进坑了,大公司特别喜欢考javascript,并且专门挑很tricky的case。javascript

javascipt的==简直就是黑魔法,之前偷懒老是用,感受也没有问题,但是准备面试就须要有寻根问底的精神。html

原题问[]==false; ![]==false console输出什么。结果是都是true java

当空数组做为判断条件时,至关于true。当空数组与布尔值直接比较时,至关于false。node

[] == ![];面试

// true
 
[ ] == true
// false

Since the left and right sides of the equality are two different types, JavaScript can't compare them directly. Hence, under the hood, JavaScript will convert them to compare. First, the right side of the equality will be cooereced to a number and number of true would be 1.数组

After that, JavaScript implementation will try to convert [] by usingtoPrimitive (of JavaScript implementation). Since [].valueOf is not primitive, it will use toString and will get ""ecmascript

Now you are comparing "" == 1 and still, the left and right are not the same type. Hence, the left side will be converted again to a number and empty string will be 0.ide

Finally, they are of same type. You are comparing 0 === 1 which will be false.函数

通常作逻辑判断应该是转换为bool类型,javascript最终倒是转换为数字来比较。post

 

1.一个数字与一个字符串,字符串转换成数字以后,进行比较。
2. true转换为1,false转换为0进行比较。// 1==true返回true;2==true返回false;
3. 数组会被转换为原始类型以后进行比较(先valueof,不行的话再toString)。var a = [1,2], a==true这里的a会被转换为"1,2"又由于true被转成了数字,因此这里的a最终被转成Number(a),也就是NaN。
结合上面3点得出a==true最终变成 NaN==1 ,因此返回false。[1]==true;//true;[2]==true;//false!!

 

参见ecma262:http://www.ecma-international.org/ecma-262/6.0/index.html#sec-abstract-equality-comparison

7.2.12Abstract Equality Comparison

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

    1. ReturnIfAbrupt(x).
    2. ReturnIfAbrupt(y).
    3. If Type(x) is the same as Type(y), then
      1. Return the result of performing Strict Equality Comparison x === y.
    4. If x is null and y is undefined, return true.
    5. If x is undefined and y is null, return true.
    6. If Type(x) is Number and Type(y) is String,
      return the result of the comparison x == ToNumber(y).
    7. If Type(x) is String and Type(y) is Number,
      return the result of the comparison ToNumber(x) == y.
    8. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
    9. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
    10. If Type(x) is either String, Number, or Symbol and Type(y) is Object, then
      return the result of the comparison x == ToPrimitive(y).
    11. If Type(x) is Object and Type(y) is either String, Number, or Symbol, then
      return the result of the comparison ToPrimitive(x) == y.
    12. Return false.

下面是转载的相关内容 

 

 

在js中,想要将对象转换成原始值,必然会调用toPrimitive()内部函数,那么它是如何工做的呢?

    该函数形式以下:

toPrimitive(input,preferedType?)
    input是输入的值,preferedType是指望转换的类型,他能够是字符串,也能够是数字。

    若是转换的类型是number,会执行如下步骤:

     1. 若是input是原始值,直接返回这个值;

     2. 不然,若是input是对象,调用input.valueOf(),若是结果是原始值,返回结果;

     3. 不然,调用input.toString()。若是结果是原始值,返回结果;

     4. 不然,抛出错误。

     若是转换的类型是String,2和3会交换执行,即先执行toString()方法。

    你也能够省略preferedType,此时,日期会被认为是字符串,而其余的值会被当作Number。

    综上所述,会有如下计算结果:

>[]+[]
>""
加号操做符会将preferedType当作Number,调用ES内部的toPrimitive(input,Number)方法,获得空字符串

>[]+{}
>"[object Object]"
 最终会调用双方的toString()方法,再作字符串加法

>{}+[]
>0
可是空对象加空数组就不同了,加号运算符的定义是这样的:若是其中一个是字符串,另外一个也会被转换为字符串,不然两个运算数都被转换为数字。而同时,javascript有这样的特性,若是{}既能够被认为是代码块,又能够被认为是对象字面量,那么js会把他当作代码块来看待。

这就很好解释了,{}被当作了代码块,只有+[],根据加法的定义,被转换为0,就获得告终果。

在操做符中,==,排序运算符,加减乘除,在对非原始值进行操做时,都会调用内部的toPrimitive()方法
---------------------
做者:苏雪冷音
来源:CSDN
原文:https://blog.csdn.net/suxuelengyin/article/details/82759437

 

[译]JavaScript中,{}+{}等于多少?

原文:http://www.2ality.com/2012/01/object-plus-object.html


最近,Gary Bernhardt在一个简短的演讲视频“Wat”中指出了一个有趣的JavaScript怪癖:在把对象和数组混合相加时,会获得一些你意想不到的结果.本篇文章会依次讲解这些计算结果是如何得出的.

JavaScript-wat

在JavaScript中,加法的规则其实很简单,只有两种状况:你只能把数字和数字相加,或者字符串和字符串相加,全部其余类型的值都会被自动转换成这两种类型的值. 为了可以弄明白这种隐式转换是如何进行的,咱们首先须要搞懂一些基础知识.注意:在下面的文章中提到某一章节的时候(好比§9.1),指的都是ECMA-262语言规范(ECMAScript 5.1)中的章节.

让咱们快速的复习一下.在JavaScript中,一共有两种类型的值:原始值(primitives)和对象值(objects).原始值有:undefined, null, 布尔值(booleans), 数字(numbers),还有字符串(strings).其余的全部值都是对象类型的值,包括数组(arrays)和函数(functions).

1.类型转换

加法运算符会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串,这恰好对应了JavaScript引擎内部的三种抽象操做:ToPrimitive(),ToNumber(),ToString()

1.1 经过ToPrimitive()将值转换为原始值

JavaScript引擎内部的抽象操做ToPrimitive()有着这样的签名:

    ToPrimitive(input, PreferredType?)

可选参数PreferredType能够是Number或者String,它只表明了一个转换的偏好,转换结果不必定必须是这个参数所指的类型,但转换结果必定是一个原始值.若是PreferredType被标志为Number,则会进行下面的操做来转换输入的值 (§9.1):

  1. 若是输入的值已是个原始值,则直接返回它.
  2. 不然,若是输入的值是一个对象.则调用该对象的valueOf()方法.若是valueOf()方法的返回值是一个原始值,则返回这个原始值.
  3. 不然,调用这个对象的toString()方法.若是toString()方法的返回值是一个原始值,则返回这个原始值.
  4. 不然,抛出TypeError异常.

若是PreferredType被标志为String,则转换操做的第二步和第三步的顺序会调换.若是没有PreferredType这个参数,则PreferredType的值会按照这样的规则来自动设置:Date类型的对象会被设置为String,其它类型的值会被设置为Number.

1.2 经过ToNumber()将值转换为数字

下面的表格解释了ToNumber()是如何将原始值转换成数字的 (§9.3).

参数 结果
undefined NaN
null +0
布尔值 true被转换为1,false转换为+0
数字 无需转换
字符串 由字符串解析为数字.例如,"324"被转换为324

若是输入的值是一个对象,则会首先会调用ToPrimitive(obj, Number)将该对象转换为原始值,而后在调用ToNumber()将这个原始值转换为数字.

1.3 经过ToString()将值转换为字符串

下面的表格解释了ToString()是如何将原始值转换成字符串的(§9.8).

参数 结果
undefined "undefined"
null "null"
布尔值 "true"  或者 "false"
数字 数字做为字符串,好比. "1.765"
字符串 无需转换

若是输入的值是一个对象,则会首先会调用ToPrimitive(obj, String)将该对象转换为原始值,而后再调用ToString()将这个原始值转换为字符串.

1.4 实践一下

下面的对象可让你看到引擎内部的转换过程.

var obj = {
    valueOf: function () {
        console.log("valueOf");
        return {}; // 没有返回原始值
    },
    toString: function () {
        console.log("toString");
        return {}; // 没有返回原始值
    }
}

Number做为一个函数被调用(而不是做为构造函数调用)时,会在引擎内部调用ToNumber()操做:

> Number(obj)
valueOf
toString
TypeError: Cannot convert object to primitive value 

2.加法

有下面这样的一个加法操做.

    value1 + value2

在计算这个表达式时,内部的操做步骤是这样的 (§11.6.1):

  1. 将两个操做数转换为原始值 (下面是数学表示法,不是JavaScript代码):
        prim1 := ToPrimitive(value1)
    prim2 := ToPrimitive(value2)
    PreferredType被省略,所以Date类型的值采用String,其余类型的值采用Number.
  2. 若是prim1或者prim2中的任意一个为字符串,则将另一个也转换成字符串,而后返回两个字符串链接操做后的结果.
  3. 不然,将prim1和prim2都转换为数字类型,返回他们的和.

2.1 预料到的结果

两个空数组相加时,结果是咱们所预料的:

> [] + []
''

[]会被转换成一个原始值,首先尝试valueOf()方法,返回数组自己(this):

> var arr = [];
> arr.valueOf() === arr
true

这样的结果不是原始值,因此再调用toString()方法,返回一个空字符串(是一个原始值).所以,[] + []的结果其实是两个空字符串的链接.

将一个空数组和一个空对象相加,结果也符合咱们的预期:

> [] + {}
'[object Object]'

相似的,空对象转换成字符串是这样的.

> String({})
'[object Object]'

因此最终的结果是 """[object Object]" 两个字符串的链接.

下面是更多的对象转换为原始值的例子,你能搞懂吗:

> 5 + new Number(7)
12
> 6 + { valueOf: function () { return 2 } }
8
> "abc" + { toString: function () { return "def" } }
'abcdef'

2.1 意想不到的结果

若是加号前面的第一个操做数是个空对象字面量,则结果会出乎咱们的意料(下面的代码在Firefox控制台中运行):

> {} + {}
NaN

这是怎么一回事?缘由就是JavaScript引擎将第一个{}解释成了一个空的代码块并忽略了它.NaN实际上是后面的表达式+{}计算的结果 (加号以及后面的{}).这里的加号并非表明加法的二元运算符,而是一个一元运算符,做用是将它后面的操做数转换成数字,和Number()函数彻底同样.例如:

> +"3.65"
3.65

转换的步骤是这样的:

+{}
Number({})
Number({}.toString())  // 由于{}.valueOf()不是原始值
Number("[object Object]")
NaN

为何第一个{}会被解析成代码块呢?缘由是,整个输入被解析成了一个语句,若是一个语句是以左大括号开始的,则这对大括号会被解析成一个代码块.因此,你也能够经过强制把输入解析成一个表达式来修复这样的计算结果:

> ({} + {})
'[object Object][object Object]'

另外,一个函数或方法的参数也会被解析成一个表达式:

> console.log({} + {})
[object Object][object Object]

通过前面的这一番讲解,对于下面这样的计算结果,你也应该不会感到吃惊了:

> {} + []
0

在解释一次,上面的输入被解析成了一个代码块后跟一个表达式+[].转换的步骤是这样的:

+[]
Number([])
Number([].toString())  // 由于[].valueOf()不是原始值
Number("")
0

有趣的是,Node.js的REPL在解析相似的输入时,与Firefox和Chrome(和Node.js同样使用V8引擎)的解析结果不一样.下面的输入会被解析成一个表达式,结果更符合咱们的预料:

> {} + {}
'[object Object][object Object]'
> {} + []
'[object Object]'

下面是SpiderMonkey 和 nodejs 中的结果对比.

3.其余

在大多数状况下,想要弄明白JavaScript中的+号是如何工做的并不难:你只能将数字和数字相加或者字符串和字符串相加.对象值会被转换成原始值后再进行计算.若是你想链接多个数组,须要使用数组的concat方法:

> [1, 2].concat([3, 4])
[ 1, 2, 3, 4 ]

JavaScript中没有内置的方法来“链接" (合并)多个对象.你可使用一个JavaScript库,好比Underscore:

> var o1 = {eeny:1, meeny:2};
> var o2 = {miny:3, moe: 4};
> _.extend(o1, o2)
{ eeny: 1,
  meeny: 2,
  miny: 3,
  moe: 4 }

注意:和Array.prototype.concat()方法不一样,extend()方法会修改它的第一个参数,而不是返回合并后的对象:

> o1
{ eeny: 1,
  meeny: 2,
  miny: 3,
  moe: 4 }
> o2
{ miny: 3, moe: 4 }

若是你想了解更多有趣的关于运算符的知识,你能够阅读一下“Fake operator overloading in JavaScript”(已墙).

4.参考

  1. JavaScript values: not everything is an object
 
posted @  2012-09-15 14:39 阅读(12246) 评论(6)
相关文章
相关标签/搜索