據悉,這道題好像是京東考的。
題目
var x = ?
// 如何令 x == 1 && x == 2 && x == 3
解答
object方式
var x = {
a: 1,
valueOf: function () {
return this.a++;
}
}
var x = {
a: 1,
toString: function () {
return this.a++;
}
}
array方式
var x = [1, 2, 3];
x.valueOf = function () {
return this.shift();
}
var x = [1, 2, 3];
x.toString = function () {
return this.shift();
}
var x = [1,2,3];
x.join = x.shift;
function方式
var x = function () {};
x.a = 1;
x.toString = function () {
return this.a
}
本質
請看ecmascript 262規格書
抽象相等比較算法
比較運算 x==y, 其中 x 和 y 是值,產生 true 或者 false。這樣的比較按如下方式 進行:
-
若Type(x)與Type(y)相同,則
- 若Type(x)爲Undefined,返回true。
- 若Type(x)爲Null,返回true。
-
若Type(x)爲Number,則
- 若x爲NaN,返回false。
- 若y爲NaN,返回false。
- 若x與y爲相等數值,返回true。
- 若x 爲 +0 且 y爲−0, 返回true。
- 若x 爲 −0 且 y爲+0, 返回true。
- 返回 false。
- 若 Type(x)爲 String, 則當 x 和 y 爲完全相同的字符序列(長度相等且相同 字符在相同位置)時返回 true。 否則, 返回 false。
- 若Type(x)爲Boolean, 當x和y爲同爲true或者同爲false時返回true。否 則, 返回 false。
- 當x和y爲引用同一對象時返回true。否則,返回false。
- 若x爲null且y爲undefined, 返回true。
- 若x爲undefined且y爲null, 返回true。
- 若 Type(x) 爲 Number 且 Type(y)爲 String, 返回 comparison x == ToNumber(y)的結果。
-
若 Type(x) 爲 String 且 Type(y)爲 Number,
- 返回比較 ToNumber(x) == y 的結果。
- 若 Type(x)爲 Boolean, 返回比較 ToNumber(x) == y 的結果。
- 若 Type(y)爲 Boolean, 返回比較 x == ToNumber(y)的結果。
- 若Type(x)爲String或Number,且Type(y)爲Object,返回比較x== ToPrimitive(y)的結果。
- 若 Type(x)爲 Object 且 Type(y)爲 String 或 Number, 返回比較 ToPrimitive(x) == y 的結果。
- 返回 false。
本質上,本題利用以下兩條抽象比較規則
-
若 Type(x) 爲 String 且 Type(y)爲 Number,
- 返回比較 ToNumber(x) == y 的結果。
- 若 Type(x)爲 Object 且 Type(y)爲 String 或 Number, 返回比較 ToPrimitive(x) == y 的結果。
實際上,三者都是Object,不明確此處爲何需要自行翻閱ecma262的規格書以及複習基本類型
那麼,爲什麼用valueOf,toString, 覆蓋join都能實現呢,請查看以下規格書定義:
ToPrimitive
ToPrimitive 運算符接受一個值,和一個可選的 期望類型 作參數。ToPrimitive 運算符把其值參數轉換爲非對象類型。如果對象有能力被轉換爲不止一種原語類 型,可以使用可選的 期望類型 來暗示那個類型。根據下表完成轉換:
此處我們只關注Object:
-
Object
- 返回該對象的默認值。(調用該對象的內部方法[[DefaultValue]]一樣)
關注[[DefaultValue]](hint)
對象內部方法[[DefaultValue]](hint)
當用字符串 hint 調用 O 的 [[DefaultValue]] 內部方法,採用以下步驟:
- 令 toString 爲用參數 "toString" 調用對象 O 的 [[Get]] 內部方法的結果。
-
如果 IsCallable(toString) 是 true,則
- 令 str 爲用 O 作爲 this 值,空參數列表調用 toString 的 [[Call]] 內部方法的結果.
- 如果 str 是原始值,返回 str。
- 令 valueOf 爲用參數 "valueOf" 調用對象 O 的 [[Get]] 內部方法的結果。
-
如果 IsCallable(valueOf) 是 true,則
- 令 val 爲用 O 作爲 this 值,空參數列表調用 valueOf 的 [[Call]] 內部方法的結果。
- 如果 val 是原始值,返回 val。
- 拋出一個 TypeError 異常。
當用數字 hint 調用 O 的 [[DefaultValue]] 內部方法,採用以下步驟:
- 令 valueOf 爲用參數 "valueOf" 調用對象 O 的 [[Get]] 內部方法的結果。
-
如果 IsCallable(valueOf) 是 true,則
- 令 val 爲用 O 作爲 this 值,空參數列表調用 valueOf 的 [[Call]] 內部方法的結果。
- 如果 val 是原始值,返回 val。
- 令 toString 爲用參數 "toString" 調用對象 O 的 [[Get]] 內部方法的結果。
-
如果 IsCallable(toString) 是 true,則
- 令 str 爲用 O 作爲 this 值,空參數列表調用 toString 的 [[Call]] 內部方法的結果。
- 如果 str 是原始值,返回 str。
- 拋出一個 TypeError 異常。
當不用 hint 調用 O 的 [[DefaultValue]] 內部方法,O 是 Date 對象的情況下 彷彿 hint 是字符串一樣解釋它的行爲,除此之外彷彿 hint 是數字一樣解釋它 的行爲。
上面說明的 [[DefaultValue]] 在原生對象中只能返回原始值。如果一個宿主對象 實現了它自身的 [[DefaultValue]] 內部方法,那麼必須確保其 [[DefaultValue]] 內部方法只能返回原始值。
既然已經知道了規則,那麼我們需要關注以下兩個方法。
關注valueOf及toString方法
爲何複寫join一樣有效
規格書中規定當調用toString方法時, 以 "join" 作爲參數調用 array 的 [[Get]] 內部方法並輸出結果。
結論
本題考查的是類型轉換後獲取原始值進行對比。