44道JavaScript送命題

很久以前看過一個老外寫的帖子,JavaScript Puzzlers!,直譯就是JavaScript難題,裡面列舉了44道JavaScript選擇題,大部分都是讓人摸不著頭腦的題目,需要仔細琢磨一番才能得到正確答案。也有一些作者也沒有解釋清除,直接通過實驗給出答案了。

這44個問題是在ECMA 262(5.1)環境下,瀏覽器中試驗的,如果是node環境下可能不同。這是因為二者環境差異,比如node環境下頂層變數是global,瀏覽器環境下則是windows。

本文部分內容也參考了文章Javascript 變態題解析

1. map&parseInt傳參

[“1”, “2”, “3”].map(parseInt)結果是什麼?

map方法指定一個回調函數,重新創建一個由回調函數返回值組成的新數組。該方法的原型是:

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array 
}[, thisArg])

map接受2個參數,一個是回調函數callback,一個是回調函數的this值。

解釋如下:

  • callback:生成新數組元素的函數,有三個參數
  • currentValue:callbac當前正在處理的元素
  • index:callback當前正在處理的當前元素的索引
  • array:map方法調用的數組本身
  • thisArg:執行callback函數時值被當做this

Number.parseInt接受兩個參數,原型Number.parseInt(string[, radix]),一個是要解析的值,一般是字元串,如果不是的話,使用toString方法將它轉化為字元串。參數radix,是一個介於236之間的整數,如果省略該值或者為0,則按照10進位來解析,也就是說默認值是10,如果是「0x」或者「0X」開頭,則以16進位為基數。如果小於2或者大於36,則parseInt返回NaN。

也就是說[].map(parseInt)這種寫法根本就是想當然的,本題相當於下面的三句:

parseInt(‘1’, 0);
parseInt(‘2’, 1);
parseInt(‘3’, 2);

這三句只有第一句會把第二個參數0默認為10,剩下兩句都不滿足radix參數介於2到36之間,所有返回[1, NaN, NaN]。另外,如果想得到正確的結果,應該這樣寫[“1”, “2”, “2”].map(i => parseInt(i))。

2. typeof和instanceof

運行[typeof null, null instanceof Object]這個表達式結果是什麼?,這個主要考察typeof,instanceof兩個操作符,前者是返回一個字元串表示未經計算的操作數的類型,後者是判斷null的原型鏈中是否出現了Object的構造函數的property。

這兩個操作符用來判斷類型,前者常用來判斷字面量,後者用來判斷對象的類型,但是兩個都有缺陷,詳見另一篇文章《javascript中判斷數據類型》

但是null是一個比較特殊的值,type of null返回的是「objec」,這是因為JavaScript最初實現中,值是由一個表示類型的標籤和實際數值表示的。對象的類型標籤是0,由於null代表是空指針(大多數平台下值為0x00),因此null的類型標籤是0,typeof null也就返回「object」。

null是所有原型鏈的最頂端,null instanceof Object返回false,假設null這個值有構造函數Null,obj instanceof Null才會返回true。

所以上面表達式返回[“object”, false]。

3.reduce&Math.pow傳參

表達式[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]返回什麼?

 和第1題有些類似。arr.reduce方法對數組中每個元素執行一個自定義的reducer函數(升序執行),並將結果匯總為單個值,這個常常用來累加一個數組中的數字。原型如下:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]) 
  • callback:數組中每個值要執行的函數,有四個參數。
  • accumulator:它是上一次回調函數時得到的值,或者initialValue。
  • currentValue:數組中正在處理的當前元素。
  • index:可選,數組中正在處理的元素的索引,如果提供了initialValue,則起始索引號為0,否則從索引1開始。
  • array:調用reduce()的數組
  • initialValue:作為第一次調用callback時的第一個參數的值。如果沒有提供初始值,則將使用數組中的第一個元素。在沒有初始值的空數組上調用reduce將報錯。

注意:如果沒有提供initialValue,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供initialValue,從索引0開始。

回調函數第一次執行時,accumulator 和currentValue的取值有兩種情況:

  1. 如果調用reduce()時提供了initialValueaccumulator取值為initialValuecurrentValue取數組中的第一個值;如果沒有提供 initialValue,那麼accumulator取數組中的第一個值,currentValue取數組中的第二個值。
  2. 如果數組為空且沒有提供initialValue,會拋出TypeError 。如果數組僅有一個元素(無論位置如何)並且沒有提供initialValue, 或者有提供initialValue但是數組為空,那麼此唯一值將被返回並且callback不會被執行。

Math.pow(base, exponent),返回基數base的指數exponent次冪,即baseexponent。

上面表達式調用reduce方法的時候沒有提供initialValue,從索引1開始執行,第一次執行的時候accumulator取arr[0],這裡是3,currentValue取第二個值,這裡是2,傳給Math.pow,得到9。

第二個表達式是在空數組上調用reduce,並且沒有提供initialValue,所以拋出錯誤:VM146:1 Uncaught TypeError: Reduce of empty array with no initial value at Array.reduce (<anonymous>)。

最後整個表達式的結果還是拋出錯誤。

4. 優先順序

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

上面的表達式輸出結果是什麼?這個問題考察的是加號和三元運算的優先順序,由於加號的優先順序高於三元表達式,所以實際執行的是:

console.log('Value is true' ? 'Something' : 'Nothing'); 

因此最後輸出「Something」。

5.變數提升問題

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

上面表達式輸出什麼?

這個是考察var變數提升問題,使用var申明的變數會提神到函數頂部,但是並不會初始化,這個是JavaScript內部機制。於是上面語句相當於:

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

name的聲明放在了函數頂部,但是值是undefined。因為程式碼又放在一個閉包里,用外層那個name = 「world」是不能訪問的,閉包有隔離變數的作用。最後,上面的語句輸出「Goodbye Jack」。

6. JavaScript能表示的最大數

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

上面表達式輸出什麼?

乍一看是100,其實是干擾,這考察的不是循環,var變數啥的,而是JavaScript能表示的最大的數字是253,即次冪表達式Math.pow(2, 53)在這個最大數的基礎上加上一個整數得到的結果都是不準確的。看下面的例子:

    var END = Math.pow(2, 53);
    var START = END - 5;
    var count = 0;
    console.log(END);                   //      9007199254740992
    console.log(END + 7);               //      9007199254740997
    console.log(START++ <= END, START); // true 9007199254740988
    console.log(START++ <= END, START); // true 9007199254740989
    console.log(START++ <= END, START); // true 9007199254740990
    console.log(START++ <= END, START); // true 9007199254740991
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992 

縮小了演示範圍,END已經是最大值9007199254740992,END+7,應該得到9007199254740998,其實是得到9007199254740997,這是一個錯誤的計算結果。如果在START的基礎上累加(每次加1),到第五次(包含第五次)得到的結果都是9007199254740992。所以在原題的i++過程中,運行到第100次之後每次得到的值都是9007199254740992,都滿足i<=END,也就是說這是一個死循環,程式一直運行,得不到任何結果。

7. 稀疏數組問題

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

這個表達式的結果是什麼?

這個問題考察的是稀疏數組,稀疏數組中的未賦值的元素是空,並且filter會忽略這些元素,所以上面filter語句返回的是空數組[]。使用console.log()語句輸出稀疏數組如下,可以看到console.log()語句也會忽略掉空位。

 

所以本題輸出的結果是[]。

 8. 數字精度問題

var two   = 0.2;
var one   = 0.1;
var eight = 0.8;
var six   = 0.6;
console.log([two - one === one, eight - six === two])

上面表達式輸出結果是什麼?

這個考察的是JavaScript數字精度問題,JavaScript的Number類型為雙精度IEEE 754 64位浮點類型,0.1不能精確的轉換成二進位,會有溢出,遵循IEEE 754標準的語言都有這個毛病,包括java。這個問題造成0.1 + 1.2!= 0.3,0.8 – 0.6 != 0.2等等。這裡先這樣簡單解釋一下,下次專門寫一篇來解釋這個問題。

本題輸出結果是[true, false]。

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'));

上面的語句輸出什麼?

這個其實考察的是字面量和對象是否恆相等的問題,case語句是使用恆等(===)來判斷的,而『A』 !== new Strting(‘A’)返回false,所以最後輸出『Do not know』。判斷恆等是三個等號’A’ == new String(‘A’)返回true,而兩個等號會調用對象的toString()方法,’A’==new String(‘A’)返回true,如下圖:

10. String()函數

function showCase2(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!');
    }
}
showCase2(String('A')); 

上面語句輸出什麼?String(“A”)不會創建一個對象,調用String方法返回一個字元串”A”,所以輸出「Case A」。

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); 

上面的map結果是什麼?

這個題目是考察求余運算符,前兩個數字7,4沒什麼好說的。『13』%2,會把字元串轉換成整形參與計算,得到1;-9%2會保留負號得到-1,符號是和第一個操作數保持一致,第二個操作數的符號會忽略,所以返回false,Infinity參與計求余計算得到NaN。所以最終結果是[true, true, true, false, false]。

12. parseInt

console.log(parseInt(3, 8));
console.log(parseInt(3, 2));
console.log(parseInt(3, 0)); 

上面表達式分別輸出什麼?

這裡和第一題一樣,再一次考察parseInt這個函數,把題目翻譯翻譯是問:把8進位里的3轉化成10進位整數是3;把2進位中的3轉換成10進位整形得到NaN,因為2進位中沒有3;把10進位中的3轉換成10進位整數還是3;所以最終結果是[3, NaN, 3]。

13. Array.prototype

console.log(Array.isArray(Array.prototype)); 

上面表達式輸出結果是什麼?

這個是是一個非常容易忽略的知識點Array.property,是Array函數的原型對象,還是一個數組,從下面的語句可以看出:

  

 所以本題的答案是true。

14. if語句

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

上面這個語句得到什麼?

在JavaScript中if語句比較特殊,引用MDN中的介紹:

任何一個值,只要它不是 undefinednull、 0NaN或空字元串(""),那麼無論是任何對象,即使是值為假的Boolean對象,在條件語句中都為真。

這個特性給我們寫if語句帶來很大的便利,不需要考慮if語句中的變數類型,因為只要它不是上述的幾種「沒有意義」的值,判斷都能通過,都能執行if語句。

但是非嚴格相等就不是這回事了,用==比較一個數組和true肯定得到false,所以本題結果是輸出false。

15. 對象比較問題

[]==[] 

上面語句的輸出結果是什麼?

這個考察的是對象比較問題,[]是一個空數組,數組屬於對象,對象比較無論如何比較都不相等。如下圖:

 

 上面比較對象(複雜數據)得到的結果都是false,所以本題結果是false。

16. +是字元串連接符也是加法運算

console.log('5' + 3);
console.log('5' - 3); 

加號遇到字元串的時候就是字元串連接符,會調用toString方法把另一個非字元串轉換成字元串,然後來連接,所以第一個結果是字元串『53』;第二個是減號,它的行為和加號剛好相反,它是把字元串轉換成整形,第二個輸出2。

所以本題輸出[’53’, 2]。

17. 加減運算和正負運算

console.log(1 + - + + + - + 1); 

上面表達式輸出什麼結果?

這個要搞明白這個表達式其實是1 + (-+++-+1),除了第一個+是加法,後面的+,-都是正負運算,根據正正得正,正負得負,負負得正的原則-+++-+1是1,所以最後結果得到2。

18. 還是稀疏數組

var ary = Array(3);
ary[0]=2
let s = ary.map(function(elem) { return '1'; });
console.log(s); 

上面語句輸出什麼?

這又是考察稀疏數組問題,map會忽略調稀疏數組中的空元素,輸出結果是[“1”, empty × 2]。

19. argument對象

function sidEffecting(ary) {
    arguments[1] = 10;
    ary[0] = ary[2];
}
function bar(a,b,c) {
    c = 10
    sidEffecting(arguments);
    return a + b + c;
}
console.log(bar(1,1,1)) 

上面語句輸出什麼?

這一題考察的是對argument對象的了解,argument是一個類數組對象,修改對象的屬性值會影響其他使用到對象的地方,即使變數不在同一範圍內。再加上對象屬性可以使用類似數組下標的方式來訪問,對象做了字元串到值的映射,而數組做的是數字到值的映射。

根據這些可以推導結果是21。

20. JavaScript中的最大數

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

上面的語句輸出什麼內容?

這個又一次考察JavaScript中的最大值問題,JavaScript中最大值是Math.pow(2, 53)=9007199254740992,計算過程中如果超出這個最大值範圍就不準確了,但是怎麼個不準確法,是不確定的。這裡輸出結果是111111111111111110000, 還是a的值。

21. Array.property.reverse

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

上面語句輸出什麼?

這個有些奇怪了,原文中解釋說reverse方法會返回調用這個方法的數組本身(就是this),但是x()沒有調用者,所以this指向了全局對象window。但是我在chrome中試過,這個是會報錯的,報錯資訊是:Uncaught TypeError: Cannot convert undefined or null to object at reverse (<anonymous>)。這個可能是原文寫的比較早,後面的瀏覽器修改了這個行為。

22. Number.MIN_VALUE

console.log(Number.MIN_VALUE > 0); 

這個考察Number.MIIN_VALUE的值,MDN上解釋如下:

Number.MIN_VALUE表示最小正數,即最接近 0 的正數 (實際上不會變成 0)。最大的負數是 -MIN_VALUE

所以Number.MIN_VALUE是大於0的,這裡輸出是true。

23. 強制轉換

console.log([1 < 2 < 3, 3 < 2 < 1]); 

上面表達式輸出什麼?

這裡考察的是在大於號,小於號運算中true會被強制轉換為1,false會被強制轉換成0。相當於console.log([true < 3, false < 1]),轉換後就是console.log([1 < 3, 0 < 1]),所以這裡輸出結果是[true, true]。

24. 數組字面量的字元串表示

console.log(2 == [[[2]]]); 

上面語句輸出什麼?

這個題套路深,我先說答案,是true。連原文作者都驚嘆這是什麼鬼?原文是:the most classic wtf!如果試著解釋的話,非嚴格相等==在遇到字面量[[[2]]]的時候,會視圖將它轉換成字元串然後和2比較,但是[[[2]]].toString()返回『2』,然後『2』 == 2返回true,是不是很驚喜?是不是想罵人?如下圖,這樣非嚴格相等就返回true了。

 

25. 3.和.3

console.log(3.toString());
console.log(3..toString());
console.log(3...toString()); 

上面語句輸出什麼結果?

這裡要搞清楚3.和.3都是合法的數字3.是一個省略了尾數部分0的數字,.3是一個省略了整數部分0的數字;第一句中但是toString()不是一個數字,所以第一句報錯:Uncaught SyntaxError: Invalid or unexpected token。第二句相當於(3.).toString()輸出」3「。第三句和第一句一樣報錯,原因也是一樣的,第二個.後面的一串..toString()不是一個合法的數字,於是就報錯了。

所以正確的答案是error,」3「,error。

26. var和閉包問題

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

上面的語句輸出什麼?

這一題考察的是對閉包和var變數的了解。閉包有隔離變數的作用,所以var不能提升變數,在閉包外部訪問x是失敗的,所以第二句輸出Undefined。閉包中如果不使用var聲明變數,直接不帶var,這樣申明y=1,反而y是全局的,在外部是可以訪問到變數y的,所以第一句輸出1。是不是很驚喜?

27. 正則表達式不可相互比較

var a = /123/, b = /123/;
console.log(a == b);
console.log(a === b); 

上面兩句分別輸出什麼?

這個考察的是正則表達式比較問題,雖然字面量內容相同,但是JavaScript認為這是兩個正則表達式,是對象類型,他們是不相等的。這裡輸出結果為false false。

28. 數組比較

    var a = [1, 2, 3], b = [1, 2, 3], c = [1, 2, 4];
    console.log(a ==  b);
    console.log(a === b);
    console.log(a > c);
    console.log(a < c); 

上面比較以此輸出什麼?

即使數組字面量相等,使用==或者===判斷兩個數組也不相等。而大於,小於比較是按照字典順序比較的,這裡以a<c具體如下:

  • 比較a[0]<c[0],相等,繼續比較下一位
  • 比較a[1]<c[1],相等,繼續比較下一位
  • 比較a[2]<c[2],返回true,於是a<c返回true

所以上面以此輸出false,false,false,true。

29. 構造函數的原型

var a = {}, b = Object.prototype
console.log([a.prototype === b, Object.getPrototypeOf(a) === b]); 

上面程式碼輸出什麼結果?

JavaScript中函數才有prototype屬性,對象是沒有的,對象有__proto__,指向對象的構造函數的原型,所以a.prototype是undefined。b是Object函數的原型,是一個對象,所以第一個是false。第二個是使用getPrototypeOf方法獲取對象a的原型(即a.__proto__),這個和Object.prototype相等的,所以第二個表達式返回true。

a.__proto__是對象的原型,它是瀏覽器暴露出來的屬性,JavaScript標準中沒有這個屬性。下面的語句輸出[true]:

var a = {}, b = Object.prototype
console.log([a.__proto__ === b); 

如下圖:

 

30. 函數的原型對象

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

上面的語句輸出什麼?

這還是考察函數原型問題。這f.prototype是獲取函數f的原型對象,Object.getPrototypeOf(f)是獲取對象構造函數的原型,注意函數也是對象,它的構造函數是Function(),因此f的構造函數的原型是Function.property,因此這這裡輸出false。

31. Function.name

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

上面的語句輸出什麼結果?

這道題考察的是函數的name屬性,function.name 屬性返回函數實例的名稱,這個屬性是不可寫的,因為訪問器屬性中writable屬性為false。所以這一題輸出的是[‘foo’, ‘foo’]。

32. str.replace

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

上面語句輸出什麼內容?這一題考察的是replace方法,str.replace方法的原型如下:

str.replace(regexp|substr, newSubStr|function

參數解釋如下:

  • regexp (pattern):一個正則表達式對象(即RegExp對象)或者其字面量。該正則所匹配的內容會被第二個參數的返回值替換掉。它和substr是二選一的。
  • substr (pattern):一個將被newSubStr替換的字元串。其被視為一整個字元串,而不是一個正則表達式。僅第一個匹配項會被替換。它和regexp是二選一的。
  • newSubStr (replacement):用於替換掉第一個參數在原字元串中的匹配部分的字元串。該字元串中可以內插一些特殊的變數名。參考下面的使用字元串作為參數。它和function是二選一的。
    變數名 代表的值
    $$ 插入一個 “$”。
    $& 插入匹配的子串。
    $` 插入當前匹配的子串左邊的內容。
    $’ 插入當前匹配的子串右邊的內容。
    $n 假如第一個參數是 RegExp對象,並且 n 是個小於100的非負整數,那麼插入第 n 個括弧匹配的字元串。提示:索引是從1開始
  • function (replacement):一個用來創建新子字元串的函數,該函數的返回值將替換掉第一個參數匹配到的結果。參考下面的指定一個函數作為參數。它和newSubStr是二選一的。
    變數名 代表的值
    match 匹配的子串。(對應於上述的$&。)
    p1,p2, … 假如replace()方法的第一個參數是一個RegExp 對象,則代表第n個括弧匹配的字元串。(對應於上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 這個來匹配,p1 就是匹配的 \a+,p2 就是匹配的 \b+。
    offset 匹配到的子字元串在原字元串中的偏移量。(比如,如果原字元串是 ‘abcd’,匹配到的子字元串是 ‘bc’,那麼這個參數將會是 1)
    string 被匹配的原字元串。
    NamedCaptureGroup 命名捕獲組匹配的對象

不得不說replace方法有點複雜,在這個例子中第一個參數是一個正則表達式,第二個參數是一個函數,函數參數以此是match,offset,string,NamedCaptureGroup,但是parseInt僅僅接受2個參數,所以相當於執行下面的語句:

parseInt(‘1’, 0) // 10進位里的1是1
parseInt(‘2’, 2) // 2進位里沒有2,所以NaN
parseInt(‘3’, 4) // 4進位中的3是3

所以最終結果是”1 NaN 3″,這個作者很喜歡拿parseInt說事。

33. eval函數

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));

上面的語句輸出什麼?

第一句f.name就是」f「;parent是Function.prototype,這是一個對象,它沒有name屬性,所以第二句應該是啥都不輸出;eval()函數會將傳入的字元串當做 JavaScript 程式碼進行執行,eval(f.name)相當於eval(‘f’),執行結果是輸出函數f的內容,type of計算返回function;最後一句返回空;

34. exp.test

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

上面語句輸出什麼內容?

RegExp.prototype.test()方法的參數是一個字元串,如果不是字元串會嘗試轉換成字元串。所以上面兩句相當於lowerCaseOnly.test(“null”),lowerCaseOnly.test(」Undefined「),所以返回[true, true]

35. 數組元素最後一個逗號

console.log([,,,].join(", "));

上面的表達式輸出什麼內容?

我們定義一個有三個元素的數組的時候可以這樣var arr = [1, 2, 3]; 也可以這樣var arr = [1, 2, 3,];它後面了一個逗號,但是還是三個元素。本題的關鍵點就是[, , ,],這其實是一個有三個空元素的數組,輸出 [empty × 3]。三個空元素用逗號連接起來最後輸出是兩個逗號,因為都是空元素,其實最後一個逗號後面是有一個空元素的。所以本題輸出”,,”。如下圖:

 

其實定義一個對象也可以在最後一個屬性後面加上一個逗號,例如 var obj = { name: ‘zhangsan’, age: 20, };

36. class關鍵字

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

上面的語句輸出什麼?

這一題是考察class關鍵字,我在chrome里輸出的是正確的值「Animal」,記住在定義變數或者屬性名字的時候盡量避免JavaScript關鍵字。如果屬性里有關鍵字,可以這樣使用a[“class”]。

37. 時間轉換問題

var a = new Date("epoch")
console.log(a); 

上面的表達式輸出什麼?

new Date()構造函數傳入的必須是一個時間字元串,即可以通過Date.parse()解析成功。所以本題輸出Invalid Date。

38. 函數的形參個數

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

上面表達式輸出什麼?

Function 構造器本身也是個Function。他的 ength屬性值為 1,所以a=1。該屬性 Writable: false, Enumerable: false, Configurable: true。但是new Function()是一個對象,他沒有形參,說以b=0,本題輸出false。另外函數的實參的個數可以用argument.length獲取。

39. 還是時間轉換問題

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

上面的語句輸出什麼?

直接調用函數Date(),得到的結果是一個表示時間的字元串;使用new操作符+構造函數,得到的是一個時間對象;參數是0返回的是格林威治0時,不帶參數返回的是當前時間;搞清楚這些之後這題就簡單了,a是一個時間字元串,b是一個時間對象,表示格林威治0時,c也是一個時間對象,不過是當前時間。所以本題輸出[false, false, false]

40. Math.max()&Math.min()

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

上面語句輸出什麼?

注意Max.max()和Max.min()傳入的參數是一個數組,如果不傳參,前者返回+Infinity,後者返回-Infinity。Infinity不能做比較,所以本題返回false。

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=2"),
    a4 = captureOne(wordRe,  "WORD=2");
console.log([a1 === a2, a3 === a4]);

上面表達式輸出什麼?這題考察的是正則表達式的/g選項,這個選項表示全局匹配;找到所有匹配,而不是在第一個匹配後停止。來看下面的例子:

var myRe = /ab*/g;
var str = 'abbcdefabh';
console.log(myRe.exec(str));
console.log(myRe.exec(str)); 

輸出結果如下:

 

 

從結果可以看出,執行兩次都有返回結果,分別找到字元串中兩處符合條件的匹配。

而原題中第一個正在有/g選項,第一次匹配成功之後會從當前位置往後查找,所以在執行a3 = captureOne(numRe,  “NUM=2”)的時候不是從字元串位置0開始查找的,而是從第5位開始,所有匹配就失敗了,找不出1。

所以本題輸出[true, false]。

42. Date中的month

var a = new Date("2014-03-19"),
    b = new Date(2014, 3, 19);
console.log([a.getDate() === b.getDate(), a.getMonth() === b.getMonth()]); 

上面表達式輸出什麼?這個問題考察的是Date對象中的月份問題,注意使用第二種方式定義時間對象,

new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]); 

第二個參數是monthIndex,它是從0開始的,3表示4月份,所以本題輸出[false, false]。如果初學JavaScript,感覺套路深啊套路深。

43. 正則表達式中的轉義

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

String.prototype.match 接受一個正則, 如果不是, 按照 new RegExp(obj) 轉化. 所以 . 並不會轉義,所以開頭的『//gifwrapped……』中/gif就匹配了 /.gif/,所以輸出「a gif file」

44. 變數提升

function foo(a) {
    var a;
    return a;
}
function bar(a) {
    var a = 'bye';
    return a;
}
console.log([foo('hello'), bar('hello')]); 

上面語句輸出什麼?a作為參數其實已經聲明了, 所以 var a; var a = ‘bye’ 其實就是 a; a =’bye’,所以本題輸出[“hello”, “bye”]。

 

JavaScript語法本來就有很多不合理的地方,導致書寫JavaScript很容易出錯,本文中如有錯誤,歡迎各位看官提出來。