(a==1&&a==2&&a==3)成立的實現及原理

最近的一道熱門的題目 `Can (a ==1 && a== 2 && a==3) ever evaluate to true?
`, 引發了不少人的關注與討論,本身能夠很容易想到一種實現,可是看你們討論出來的答案,其中有不少有意思的,不得不佩服一些人的腦洞,其中不少原理也值得探討。javascript

不少其餘語言的程序員對於這樣的結果,不少都歸結於 果真javascript 之類,的確與一些語言不一樣,javascript 中除了 == 外還有 ====== 叫作嚴格運算符,==叫作相等運算符。java

關於 === 於 ==

其實 JavaScript 一共提供了8個比較運算符。程序員

<   小於運算符
 >   大於運算符
 <=  小於或等於運算符
 =   大於或等於運算符
 ==  相等運算符
 === 嚴格相等運算符
 !=  不相等運算符
 !== 嚴格不相等運算符

比較操做涉及多不一樣類型的值時候,會涉及到不少隱式轉換,其中規則繁多即使是經驗老道的程序員也沒辦法徹底記住,特別是用到 == 和 != 運算時候。因此一些團隊規定禁用 == 運算符換用=== 嚴格相等。以工程標準衡量,== 帶來的便利性抵不上其帶來的成本,團隊協做時候你看到別人代碼中的 ==,有些時候須要判斷清楚做者的代碼意圖是確實須要轉型,仍是無所謂要不要轉型只是隨手寫了,增長了一些額外的成本。可是我比較喜歡的一本書 You don't know JS,中做者也寫道過一個我比較贊同的觀點es6

Many developers feel that === is more predictable, so they advocate always using that form and staying away from ==. I think this view is very shortsighted. I believe == is a powerful tool that helps your program, if you take the time to learn how it works.算法

簡譯爲數組

不少開發者認爲 === 的行爲更加容易預測,從而主張使用 === 而遠離 ==。我認爲這種觀點是很是短視的,若是你花點時間去搞清楚它的工做原理,== 將是你開發的強大工具。閉包

究竟誰對誰錯也沒有定論,可是去了解一些隱式轉換的規則,對咱們開發和調試bug都很是有幫助。這些規則規定很煩多,一個個看過來很無趣也不能全記住。咱們下面從這個問題的一些答案中去探究其中的一小部分規則。函數

題目一些答案

你們來找茬?

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if(aᅠ==1 && a== 2 &&ᅠa==3) {
    console.log("Why hello there!")
}

看到這個答案時候曾一度懷疑本身學的是假的 javascript,這個答案和隱式轉換沒有關係,能夠說它能考察你的找茬能力,注意if裏面的空格,它是一個Unicode空格字符,不被ECMA腳本解釋爲空格字符(這意味着它是標識符的有效字符)。因此它能夠解釋爲工具

var a_ = 1;
var a = 2;
var _a = 3;
if(a_==1 && a== 2 &&_a==3) {
    console.log("Why hello there!")
}

運算子是對象時候的valueOf toString 方法

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}

若是原始類型的值和對象比較,對象會轉爲原始類型的值,再進行比較。(我想到的也是這種方法),對象轉換成原始類型的值,算法是先調用valueOf方法;若是返回的仍是對象,再接着調用toString方法。咱們每次比較時候都會執行方法返回 a 的 i 屬性同時也改變 i 的值,因此上面 if 執行完之後 a 的 i屬性已經變爲了 4,這裏也表現出了 == 比較是有可能會對變量帶來反作用的this

利用數組的特性

var a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);

這個答案仍是比較巧妙的,咱們知道 array也屬於對象,應該和對象的規則同樣。關於 array 的原型鏈上的 toString 方法

對於數組對象,toString 方法返回一個字符串,該字符串由數組中的每一個元素的 toString() 返回值經調用 join() 方法鏈接(由逗號隔開)組成。

能夠看到數組 toString 會調用自己的 join方法,這裏把本身的join方法該寫爲shift,每次返回第一個元素,並且原數組刪除第一個值,正好可使判斷成立。這裏 == 比較也帶來的反作用

利用with 關鍵字

var i = 0;

with({
  get a() {
    return ++i;
  }
}) {
  if (a == 1 && a == 2 && a == 3)
    console.log("wohoo");
}

with 也是被嚴重建議不使用的對象,這裏也是利用它的特性在代碼塊裏面利用對象的 get 方法動態返回 i.

和with相似修改window的get方法

var val = 0;
Object.defineProperty(window, 'a', {
  get: function() {
    return ++val;
  }
});
if (a == 1 && a == 2 && a == 3) {
  console.log('yay');
}

咱們知道咱們用的全局變量也至關於 window對象上的一個屬性,這裏用defineProperty定義了 a的 get 也使得其動態返回值。和with 有一些相似。

es6的Symbol特性

let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};

console.log(a == 1 && a == 2 && a == 3);

ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。咱們以前在定義類的內部私有屬性時候習慣用 __xxx ,這種命名方式避免別人定義相同的屬性名覆蓋原來的屬性,有了 Symbol 以後咱們徹底能夠用 Symbol值來代替這種方法,並且徹底不用擔憂被覆蓋。

除了定義本身使用的 Symbol 值之外,ES6還提供了 11 個內置的 Symbol 值,指向語言內部使用的方法。Symbol.toPrimitive就是其中一個,它指向一個方法,表示該對象被轉爲原始類型的值時,會調用這個方法,返回該對象對應的原始類型值。這裏就是改變這個屬性,把它的值改成一個 閉包 返回的函數。

相關文章
相關標籤/搜索