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)
相關文章
相關標籤/搜索