很久以前看過一個老外寫的帖子,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,是一個介於2到36之間的整數,如果省略該值或者為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
的取值有兩種情況:
- 如果呼叫
reduce()
時提供了initialValue
,accumulator
取值為initialValue
,currentValue
取陣列中的第一個值;如果沒有提供initialValue
,那麼accumulator
取陣列中的第一個值,currentValue
取陣列中的第二個值。- 如果陣列為空且沒有提供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中的介紹:
任何一個值,只要它不是
undefined
、null
、0
、NaN
或空字串(""
),那麼無論是任何物件,即使是值為假的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 ('http://giftwrapped.com/picture.jpg'.match('.gif')) { console.log('a gif file'); } else { console.log('not a gif file'); }
String.prototype.match 接受一個正則, 如果不是, 按照 new RegExp(obj) 轉化. 所以 . 並不會轉義,所以開頭的‘http://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很容易出錯,本文中如有錯誤,歡迎各位看官提出來。