天天看點

44道JS難題,做對一半就是高手

44道JS難題,做對一半就是高手

國外某網站給出了44道JS難題,試着做了下,隻做對了17道。這些題涉及面非常廣,涵蓋JS原型、函數細節、強制轉換、閉包等知識,而且都是非常細節的東西,透過這些小細節可以折射出很多進階的JS知識點。

你可以通過傳送門(​​http://javascript-puzzlers.herokuapp.com/​​)先去測試一下你的水準,然後回來看看我的解析。為了詳細解釋這些細節,我也查閱了不少資料,彌補了很多JS知識盲點。

1. parseInt 遇上 map

["1", "2", "3"].map(parseInt)


// A. ["1", "2", "3"]
// B. [1, 2, 3]
// C. [0, 1, 2]
// D. other      

答案是D。實際上傳回的結果是 [1, NaN, NaN] ,因為 parseInt 函數隻需要兩個參數 parseInt(value, radix) ,而 map 的回調函數需要三個參數 callback(currentValue, index, array)。

MDN文檔中指明 parseInt 第二個參數是一個2到36之間的整數值,用于指定轉換中采用的基數。如果省略該參數或其值為0,則數字将以10為基礎來解析。

如果該參數小于2或者大于36,則 parseInt 傳回 NaN。此外,轉換失敗也會傳回 NaN。

現在來分析問題。parseInt("1", 0) 的結果是當作十進制來解析,傳回 1;parseInt("2", 1) 的第二個參數非法,傳回 NaN;parseInt("3", 2) 在二進制中,"3" 是非法字元,轉換失敗,傳回 NaN。

參考資料:

  • MDN: Array.prototype.map()
  • MDN: parseInt

2. 神奇的null

[typeof null, null instanceof Object]


// A. ["object", false]
// B. [null, false]
// C. ["object", true]
// D. other      

答案是A。在MDN關于 null 的文檔中也特别指出來了,typeof null 的結果是 "object",它是ECMAScript的bug,其實應該是 "null"。但這個bug由來已久,在JavaScript中已經存在了将近二十年,也許永遠不會修複,因為這牽扯到太多的Web系統,修複它會産生更多的bug,令許多系統無法正常工作。而 instanceof 運算符是用來測試一個對象在其原型鍊構造函數上是否具有 prototype 屬性,null 值并不是以 Object 原型建立出來的,是以 null instanceof Object 傳回 false。

參考資料:

  • MDN:null
  • MDN:instanceof
  • CSDN部落格:null instanceof Object 求解?

3. 憤怒的reduce

[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]


// A. an error
// B. [9, 0]
// C. [9, NaN]
// D. [9, undefined]      

答案是A。MDN文檔中關于 Array.prototype.reduce() 寫得很清楚:

如果數組為空并且沒有提供initialValue, 會抛出TypeError 。如果數組僅有一個元素(無論位置如何)并且沒有提供initialValue, 或者有提供initialValue但是數組為空,那麼此唯一值将被傳回并且callback不會被執行。

參考資料:

  • MDN:Array.prototype.reduce()

4. 該死的優先級

var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');


// A. Value is Something
// B. Value is Nothing
// C. NaN
// D. other      

答案是D。實際上輸出 "Something",因為 + 的優先級比條件運算符 condition ? val1 : val2 的優先級高。

參考資料:

  • MDN:運算符優先級

5. 神鬼莫測之變量提升

var name = 'World!';
(function () {
    if (typeof name === 'undefined') {
      var name = 'Jack';
      console.log('Goodbye ' + name);
    } else {
      console.log('Hello ' + name);
    }
})();


// A. Goodbye Jack
// B. Hello Jack
// C. Hello undefined
// D. Hello World      

答案是A。看如下MDN官方文檔的解釋:

在 JavaScript中, functions 和 variables 會被提升。變量提升是JavaScript将聲明移至作用域 scope (全局域或者目前函數作用域) 頂部的行為。

這意味着你可以在聲明一個函數或變量之前引用它,或者可以說:一個變量或函數可以在它被引用之後聲明。

是以,上面的代碼與下面這段代碼是等價的:

var name = 'World!';
(function () {
    var name;
    if (typeof name === 'undefined') {
      name = 'Jack';
      console.log('Goodbye ' + name);
    } else {
      console.log('Hello ' + name);
    }
})();      

參考資料:

  • MDN:變量提升
  • 這篇部落格解釋的比較詳細:js變量提升

6. 死循環陷阱

var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) { 
  count++;
}
console.log(count);


// A. 0
// B. 100
// C. 101
// D. other      

答案是D。在JavaScript中,2^53 是最大的值,沒有比這更大的值了。是以 2^53 + 1 == 2^53,是以這個循環無法終止。

7. 過濾器魔法

var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) {
  return x === undefined;
});


// A. [undefined x 7]
// B. [0, 1, 2, 10]
// C. []
// D. [undefined]      

答案是C。看MDN官方文檔的描述:

filter 為數組中的每個元素調用一次 callback 函數,并利用所有使得 callback 傳回 true 或 等價于 true 的值 的元素建立一個新數組。callback 隻會在已經指派的索引上被調用,對于那些已經被删除或者從未被指派的索引不會被調用。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新數組中。

參考資料:

  • MDN:Array.prototype.filter()

8. 警惕IEEE 754标準

var two = 0.2;
var one = 0.1;
var eight = 0.8;
var six = 0.6;
[two - one == one, eight - six == two]


// A. [true, false]
// B. [false, false]
// C. [true, false]
// D. other      

答案是C。JavaScript中采用雙精度浮點數格式,即IEEE 754标準。在該格式下,有些數字無法表示出來,比如:0.1 + 0.2 = 0.30000000000000004 ,這不是JavaScript的鍋,所有采用該标準的語言都有這個問題,比如:Java、Python等。

參考資料:

  • Wiki:Double-precision floating-point format

9. 字元串陷阱

function showCase(value) {
  switch(value) {
    case 'A':
      console.log('Case A');
      break;
    case 'B':
      console.log('Case B');
      break;
    case undefined:
      console.log('undefined');
      break;
    default:
      console.log('Do not know!');
  }
}
showCase(new String('A'));


// A. Case A
// B. Case B
// C. Do not know!
// D. undefined      

答案是C。在 switch 内部使用嚴格相等 === 進行判斷,并且 new String("A") 傳回的是一個對象,而 String("A") 則是直接傳回字元串 "A"。你也可以參考MDN中對原始字元串和String對象的區分:

Note that JavaScript distinguishes between String objects and primitive string values. (The same is true of Boolean and Numbers.)

String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings. JavaScript automatically converts primitives to String objects, so that it's possible to use String object methods for primitive strings. In contexts where a method is to be invoked on a primitive string or a property lookup occurs, JavaScript will automatically wrap the string primitive and call the method or perform the property lookup.

參考資料:

  • MDN:String

10. 再一次的字元串陷阱

function showCase(value) {
  switch(value) {
    case 'A':
      console.log('Case A');
      break;
    case 'B':
      console.log('Case B');
      break;
    case undefined:
      console.log('undefined');
      break;
    default:
      console.log('Do not know!');
  }
}
showCase(String('A'));


// A. Case A
// B. Case B
// C. Do not know!
// D. undefined      

答案顯然是A。與上面唯一不同的是沒有使用 new 關鍵字,是以直接傳回字元串,實際上,typeof string("A") === "string" 的結果是 true。解釋參見第9條的解釋。

11. 并非都是奇偶

function isOdd(num) {
  return num % 2 == 1;
}


function isEven(num) {
  return num % 2 == 0;
}


function isSane(num) {
  return isEven(num) || isOdd(num);
}


var values = [7, 4, "13", -9, Infinity];
values.map(isSane);


// A. [true, true, true, true, true]
// B. [true, true, true, true, false]
// C. [true, true, true, false, false]
// D. [true, true, false, false, false]      

答案是C。-9 % 2 = -1 以及 Infinity % 2 = NaN,求餘運算符會保留符号,是以隻有 isEven 的判斷是可靠的。

12. parseInt小賊

parseInt(3, 8);
parseInt(3, 2);
parseInt(3, 0);


// A. 3, 3, 3
// B. 3, 3, NaN
// C. 3, NaN, NaN
// D. other      

答案是D。實際結果是 3, NaN, 3,這個在第一個問題中解釋的很清楚了。

13. 數組原型是數組

Array.isArray( Array.prototype )


// A. true
// B. false
// C. error
// D. other      

答案是A。一個鮮為人知的事實:其實 Array.prototype 也是一個數組。這點在MDN文檔中提到過。

參考資料:

  • MDN:Array.isArray()

14. 一言難盡的強制轉換

var a = [0];
if ([0]) {
  console.log(a == true);
} else {
  console.log("wut");
}


// A. true
// B. false
// C. "wut"
// D. other      

答案是B。這個是JavaScript中強制轉換的經典案例,關于強制轉換不是一兩句話可以跟你說清楚的,我建議你系統性的學習一下,推薦你看看《你不知道的JavaScript-中卷》這本書,如果不舍得買書,github上有英文原版:You-Dont-Know-JS,深入了解之後你就是高手了。

好了,回到目前這個問題。當 [0] 需要被強制轉成 Boolean 的時候會被認為是 true。是以進入第一個 if 語句,而 a == true 的轉換規則在ES5規範的第11.9.3節中已經定義過,你可以自己詳細探索下。

規範指出,== 相等中,如果有一個操作數是布爾類型,會先把他轉成數字,是以比較變成了 [0] == 1;同時規範指出如果其他類型和數字比較,會嘗試把這個類型轉成數字再進行寬松比較,而對象(數組也是對象)會先調用它的 toString() 方法,此時 [0] 會變成 "0",然後将字元串 "0" 轉成數字 0,而 0 == 1 的結果顯然是 false。

參考資料:

  • ES5規範:11.9.3
  • 《你不知道的JavaScript-中卷》

15. 撒旦之子“==”

[]==[]


// A. true
// B. false
// C. error
// D. other      

答案是B。ES5規範11.9.3.1-f指出:如果比較的兩個對象指向的是同一個對象,就傳回 true,否則就傳回 false,顯然,這是兩個不同的數組對象。

參考資料:

  • ES5規範:11.9.3.1
  • 《你不知道的JavaScript-中卷》

16. 加号 VS 減号

'5' + 3;
'5' - 3;


// A. "53", 2
// B. 8, 2
// C. error
// D. other      

答案是A。"5" + 2 = "52" 很好了解,+ 運算符中隻要有一個是字元串,就會變成字元串拼接操作。你不知道的是,- 運算符要求兩個操作數都是數字,如果不是,會強制轉換成數字,是以結果就變成了 5 - 2 = 3。

參考資料:

  • 《你不知道的JavaScript-中卷》,第四章:4.4.2 字元串和數字之間的隐式強制類型轉換

17. 打死那個瘋子

1 + - + + + - + 1


// A. 2
// B. 1
// C. error
// D. other      

答案是A。這個隻能出現在示例代碼中,如果你發現哪個瘋子寫了這個在生産代碼中,打死他就行了。你隻要知道 + 1 = 1和- 1 = -1,注意符号之間的空格。兩個減号抵消,是以最終結果等效于 1 + 1 = 2。或者你也可以在符号之間插入 0 來了解,即 1 + 0 - 0 + 0 + 0 + 0 - 0 + 1,這樣你就一目了然了吧!千萬别寫這樣的代碼,因為可能會被打死!

18. 淘氣的map

var ary = Array(3);
ary[0] = 2;
ary.map(function(elem) {
  return "1";
});


// A. [2, 1, 1]
// B. ["1", "1", "1"]
// C. [2, "1", "1"]
// D. other      

答案是D。實際上結果是 ["1", undefined x 2],因為規範寫得很清楚:

map 方法會給原數組中的每個元素都按順序調用一次 callback 函數。callback 每次執行後的傳回值組合起來形成一個新數組。callback 函數隻會在有值的索引上被調用;那些從來沒被賦過值或者使用 delete 删除的索引則不會被調用。

參考資料:

  • MDN: Array.prototype.map()

19. 統統算我的

function sidEffecting(ary) {
  ary[0] = ary[2];
}


function bar(a, b, c) {
  c = 10;
  sidEffecting(arguments);
  return a + b + c;
}


bar(1, 1, 1);


// A. 3
// B. 12
// C. error
// D. other      

答案是D。實際上結果是 21。在JavaScript中,參數變量和 arguments 是雙向綁定的。改變參數變量,arguments 中的值會立即改變;而改變 arguments 中的值,參數變量也會對應改變。

20. 損失精度的IEEE 754

var a = 111111111111111110000;
var b = 1111;
console.log(a + b);


// A. 111111111111111111111
// B. 111111111111111110000
// C. NaN
// D. Infinity      

答案是B。這是IEEE 754規範的黑鍋,不是JavaScript的問題。表示這麼大的數占用過多位數,會丢失精度,學過計算機組成原理的應該知道是怎麼回事。

參考資料:

  • Wiki:Double-precision floating-point format

21. 反轉世界

var x = [].reverse;
x();


// A. []
// B. undefined
// C. error
// D. window      

答案是D。MDN規範關于 reverse 的描述:

reverse 方法颠倒數組中元素的位置,并傳回該數組的引用。

而這裡調用的時候沒有制定數組,是以預設的 this 就是 window,是以最後結果傳回的是 window。

參考資料:

  • MDN:Array.prototype.reverse()

22. 最小的正值

Number.MIN_VALUE > 0


// A. false
// B. true
// C. error
// D. other      

答案是B。看規範描述吧:

MIN_VALUE屬性是 JavaScript 裡最接近 0 的正值,而不是最小的負值。

MIN_VALUE的值約為 5e-324。小于 MIN_VALUE

("underflow values") 的值将會轉換為 0。

因為 MIN_VALUE是 Number 的一個靜态屬性,是以應該直接使用:Number.MIN_VALUE,而不是作為一個建立的 Number執行個體的屬性。

參考資料:

  • MDN:Number.MIN_VALUE

23. 謹記優先級

[1 < 2 < 3, 3 < 2 < 1]


// A. [true, true]
// B. [true, false]
// C. error
// D. other      

答案是A。<和>的優先級都是從左到右,是以 1 < 2 < 3 會先比較 1 < 2,這會得到 true,但是 < 要求比較的兩邊都是數字,是以會發生隐式強制轉換,将 true 轉換成 1,是以最後就變成了比較 1 < 3,結果顯然為 true。同理可以分析後者。

參考資料:

  • MDN:運算符優先級

24. 坑爹中的戰鬥機

// the most classic wtf
2 == [[[2]]]


// A. true
// B. false
// C. undefined
// D. other      

答案是A。根據ES5規範,如果比較的兩個值中有一個是數字類型,就會嘗試将另外一個值強制轉換成數字,再進行比較。而數組強制轉換成數字的過程會先調用它的 toString方法轉成字元串,然後再轉成數字。是以 [2]會被轉成 "2",然後遞歸調用,最終 [[[2]]] 會被轉成數字 2。

25. 小數點魔術

3.toString();
3..toString();
3...toString();


// A. "3", error, error
// B. "3", "3.0", error
// C. error, "3", error
// D. other      

答案是C。點運算符會被優先識别為數字常量的一部分,然後才是對象屬性通路符。是以 3.toString() 實際上被JS引擎解析成 (3.)toString(),顯然會出現文法錯誤。但是如果你這麼寫 (3).toString(),人為加上括号,這就是合法的。

26. 自動提升為全局變量

(function() {
  var x = y = 1;
})();
console.log(y);
console.log(x);


// A. 1, 1
// B. error, error
// C. 1, error
// D. other      

答案是C。很經典的例子,在函數中沒有用 var 聲明變量 y,是以 y 會被自動建立在全局變量 window下面,是以在函數外面也可以通路得到。而 x 由于被 var 聲明過,是以在函數外部是無法通路的。

27. 正規表達式執行個體

var a = /123/;
var b = /123/;
a == b;
a === b;


// A. true, true
// B. true, false
// C. false, false
// D. other      

答案是C。每個字面的正規表達式都是一個單獨的執行個體,即使它們的内容相同。

28. 數組也愛比大小

var a = [1, 2, 3];
var b = [1, 2, 3];
var c = [1, 2, 4];


a == b;
a === b;
a > c;
a < c;


// A. false, false, false, true
// B. false, false, false, false
// C. true, true, false, true
// D. other      

答案是A。數組也是對象,ES5規範指出如果兩個對象進行相等比較,隻有在它們指向同一個對象的情況下才會傳回 true,其他情況都傳回 false。而對象進行大小比較,會調用 toString 方法轉成字元串進行比較,是以結果就變成了字元串 "1,2,3" 和 "1,2,4" 按照字典序進行比較了(你若不信,可以重制兩個變量的 toString 方法,進行測試)。

29. 原型把戲

var a = {};
var b = Object.prototype;


[a.prototype === b, Object.getPrototypeOf(a) == b]


// A. [false, true]
// B. [true, true]
// C. [false, false]
// D. other      

答案是A。對象是沒有 prototype 屬性的,是以 a.prototype 是 undefined,但我們可以通過 Object.getPrototypeOf 方法來擷取一個對象的原型。

30. 構造函數的函數

function f() {}
var a = f.prototype;
var b = Object.getPrototypeOf(f);
a === b;


// A. true
// B. false
// C. null
// D. other      

答案是B。這個解釋起來有點繞口,我們先來看另外一段代碼:

function Person() {}
var p = new Person();


var a = p.__proto__;
var b = Object.getPrototypeOf(p);
var c = Person.prototype;
console.log(a === b, a === c, b === c);
// true, true, true


var d = Person.__proto__;
var e = Object.getPrototypeOf(Person);
var f = Function.prototype;
console.log(d === e, d === f, e === f);
// true, true, true      

首先你要明白,任何函數都是 Function 的執行個體,而p是函數 Person 的執行個體,Object.getPrototypeOf 會擷取構造目前對象的原型。是以 Object.getPrototypeOf(p) === Person.prototype,而 Object.getPrototypeOf(Person) === Function.prototype,是以答案就很明顯了。

我解釋的不是很好,如果讀者有更好的解釋,歡迎評論。

31. 禁止修改函數名

function foo() {}
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name];


// A. error
// B. ["", ""]
// C. ["foo", "foo"]
// D. ["foo", "bar"]      

答案是C。函數名是禁止修改的,規範寫的很清楚,是以這裡的修改無效。

參考資料:

  • MDN:Function.name

32. 替換陷阱

"1 2 3".replace(/\d/g, parseInt);


// A. "1 2 3"
// B. "0 1 2"
// C. "NaN 2 3"
// D. "1 NaN 3"      

答案是D。如果 replace 方法第二個參數是一個函數,則會在比對的時候多次調用,第一個參數是比對的字元串,第二個參數是比對字元串的下标。是以變成了調用 parseInt(1, 0)、parseInt(2, 2)和parseInt(3, 4),結果你就懂了。

參考資料:

  • MDN:String.prototype.replace()

33. Function的名字

function f() {}
var parent = Object.getPrototypeOf(f);
console.log(f.name);
console.log(parent.name);
console.log(typeof eval(f.name));
console.log(typeof eval(parent.name));


// A. "f", "Empty", "function", "function"
// B. "f", undefined, "function", error
// C. "f", "Empty", "function", error
// D. other      

答案是C。根據第30題的解釋,我們知道代碼中的 parent 實際上就是 Function.prototype,而它在控制台中輸出為:

function () {
  [native code]
}      

它的 name 屬性是 "",是以你 eval("")是得不到任何東西的。

34. 正則測試陷阱

var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]


// A. [true, false]
// B. error
// C. [true, true]
// D. [false, true]      

答案是C。test 方法的參數如果不是字元串,會經過抽象 ToString操作強制轉成字元串,是以實際上測試的是字元串 "null" 和 "undefined"。

35. 逗号定義數組

[,,,].join(", ")


// A. ", , , "
// B. "undefined, undefined, undefined, undefined"
// C. ", , "
// D. ""      

答案是C。JavaScript允許用逗号來定義數組,得到的數組是含有3個 undefined 值的數組。MDN關于 join 方法的描述:

所有的數組元素被轉換成字元串,再用一個分隔符将這些字元串連接配接起來。如果元素是undefined 或者null, 則會轉化成空字元串。

參考資料:

  • MDN:Array.prototype.join()

36. 保留字 class

var a = {class: "Animal", name: "Fido"};
console.log(a.class);


// A. "Animal"
// B. Object
// C. an error
// D. other      

答案是D。實際上真正的答案取決于浏覽器。class 是保留字,但是在Chrome、Firefox和Opera中可以作為屬性名稱,在IE中是禁止的。另一方面,其實所有浏覽器基本接受大部分的關鍵字(如:int、private、throws等)作為變量名,而class是禁止的。

37. 無效日期

var a = new Date("epoch");


// A. Thu Jan 01 1970 01:00:00 GMT+0100(CET)
// B. current time
// C. error
// D. other      

答案是D。實際結果是 Invalid Date,它實際上是一個Date對象,因為 a instance Date 的結果是 true,但是它是無效的Date。Date對象内部是用一個數字來存儲時間的,在這個例子中,這個數字是 NaN。

38. 神鬼莫測的函數長度

var a = Function.length;
var b = new Function().length;
console.log(a === b);


// A. true
// B. false
// C. error
// D. other      

答案是B。實際上a的值是1,b的值是0。還是繼續來看MDN文檔關于 Function.length 的描述吧!

Function構造器的屬性:

Function 構造器本身也是個Function。他的 length 屬性值為 1 。該屬性 Writable: false, Enumerable: false, Configurable: true。

Function原型對象的屬性:

Function原型對象的 length 屬性值為 0 。

是以,在本例中,a代表的是 Function 構造器的 length 屬性,而b代表的是 Function 原型的 length 屬性。

參考資料:

  • MDN:Function.length

39. Date的面具

var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c];


// A. [true, true, true]
// B. [false, false, false]
// C. [false, true, false]
// D. [true, false, false]      

答案是B。先看MDN關于Date對象的注意點:

需要注意的是隻能通過調用 Date 構造函數來執行個體化日期對象:以正常函數調用它(即不加 new 操作符)将會傳回一個字元串,而不是一個日期對象。另外,不像其他JavaScript 類型,Date 對象沒有字面量格式。

是以a是字元串,b和c是Date對象,并且b代表的是1970年那個初始化時間,而c代表的是目前時間。

參考資料:

  • MDN:Date

40. min與max共舞

var min = Math.min();
var max = Math.max();
console.log(min < max);


// A. true
// B. false
// C. error
// D. other      

答案是B。看MDN文檔,對 Math.min的描述:

如果沒有參數,結果為Infinity。

對 Math.max 的描述:

如果沒有參數,結果為-Infinity。

參考資料:

  • MDN:Math.min
  • MDN:Math.max

41. 警惕全局比對

function captureOne(re, str) {
  var match = re.exec(str);
  return match && match[1];
}


var numRe = /num=(\d+)/ig,
      wordRe = /word=(\w+)/i,
      a1 = captureOne(numRe, "num=1"),
      a2 = captureOne(wordRe, "word=1"),
      a3 = captureOne(numRe, "NUM=1"),
      a4 = captureOne(wordRe, "WORD=1");


[a1 === a2, a3 === a4]


// A. [true, true]
// B. [false, false]
// C. [true, false]
// D. [false, true]      

答案是C。看MDN關于 exec 方法的描述:

當正規表達式使用 "g" 标志時,可以多次執行 exec 方法來查找同一個字元串中的成功比對。當你這樣做時,查找将從正規表達式的  lastIndex 屬性指定的位置開始。

是以a3的值為 null。

參考資料:

  • MDN:RegExp.prototype.exec()

42. 最熟悉的陌生人

var a = new Date("2014-03-19");
var b = new Date(2014, 03, 19);
[a.getDay() == b.getDay(), a.getMonth() == b.getMonth()]


// A. [true, true]
// B. [true, false]
// C. [false, true]
// D. [false, false]      

答案是D。先看MDN關于Date的一個注意事項:

當Date作為構造函數調用并傳入多個參數時,如果數值大于合理範圍時(如月份為13或者分鐘數為70),相鄰的數值會被調整。比如 new Date(2013, 13, 1)等于new Date(2014, 1, 1),它們都表示日期2014-02-01(注意月份是從0開始的)。其他數值也是類似,new Date(2013, 2, 1, 0, 70)等于new Date(2013, 2, 1, 1, 10),都表示時間2013-03-01T01:10:00。

此外,getDay 傳回指定日期對象的星期中的第幾天(0~6),是以,你懂的。

參考資料:

  • MDN:Date

43. 比對隐式轉換

if("http://giftwrapped.com/picture.jpg".match(".gif")) {
  console.log("a gif file");
} else {
  console.log("not a gif file");
}


// A. "a gif file"
// B. "not a gif file"
// C. error
// D. other      

答案是A。看MDN對 match 方法的描述:

如果傳入一個非正規表達式對象,則會隐式地使用 new RegExp(obj)

将其轉換為正規表達式對象。

是以我們的字元串 ".gif" 會被轉換成正則對象 /.gif/,會比對到 "/gif"。

參考資料:

  • MDN:String.prototype.match()

44. 重複聲明變量

function foo(a) {
  var a;
  return a;
}


function bar(a) {
  var a = "bye";
  return a;
}


[foo("hello"), bar("hello")]


// A. ["hello", "hello"]
// B. ["hello", "bye"]
// C. ["bye", "bye"]
// D. other      

答案是B。一個變量在同一作用域中已經聲明過,會自動移除 var 聲明,但是指派操作依舊保留,結合前面提到的變量提升機制,你就明白了。

參考資料:

  • MDN:var

結束語

如果你對我的解釋有疑問,請給我留言,歡迎大家一起讨論這些JS難題。

本文完~

繼續閱讀