前言
題目來自JavaScript Puzzlers,涉及的知識(shí)點(diǎn)很廣,本文的解題過程盡量做到詳細(xì)易懂。由于文章篇幅較長(zhǎng),推薦先馬后看,相信一定可以在文章中找到不熟或者概念模糊的知識(shí)點(diǎn)。(全篇共44題,筆者連蒙帶猜一刷成績(jī)?yōu)?7/44,有幾題都是蒙對(duì)的,勉強(qiáng)及格)
正文
第一題
["1", "2", "3"].map(parseInt)
考察知識(shí)點(diǎn):
1.map函數(shù)
2.parseInt函數(shù)
解析:map函數(shù)的callback默認(rèn)有三個(gè)參數(shù),分別為currentValue、currentIndex、arrary。此處用parseInt作為callback,而parseInt只能接收兩個(gè)參數(shù),所以callback中的默認(rèn)第三個(gè)參數(shù)array被忽略。如果把map的回調(diào)拆分開,此時(shí)相當(dāng)于這樣:
parseInt("1", 0) // 1
parseInt("2", 1) // NaN
parseInt("3", 2) // NaN
parseInt的第二個(gè)參數(shù)取值范圍是2~36,此參數(shù)省略或者為0時(shí),以十進(jìn)制方式解析,所以“parseInt("1", 0)”返回1。
“parseInt("2", 1)”第二個(gè)參數(shù)為1,不在取值范圍內(nèi),所以返回NaN。
“ parseInt("3", 2)”以二進(jìn)制的方式解析字符串3,但是二進(jìn)制中的有效數(shù)字只有0和1,所以返回NaN。
答案:[1, NaN, NaN]
第二題
[typeof null, null instanceof Object]
考察知識(shí)點(diǎn):
1.null
2.instanceof
解析:原型鏈的源頭為null,所有對(duì)象都是通過null派生出來的,null本身也被定義為對(duì)象,所以“typeof null”返回object字符串。instanceof用于判斷構(gòu)造函數(shù)的prototype屬性是否在對(duì)象的原型鏈上,因?yàn)閚ull為原型鏈頂端,而“Object.prototype”有值,顯然“null instanceof Object”返回false。
答案:["object", false]
第三題
[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]
考察知識(shí)點(diǎn):
1.reduce
解析:reduce函數(shù)的callback默認(rèn)有四個(gè)參數(shù),分別為accumulator、currentValue、currentIndex、array。此處用Math.pow作為callback,而Math.pow只能接收兩個(gè)參數(shù),所以currentIndex和array被忽略。
如果沒有提供initialValue,reduce 會(huì)從索引1的地方開始執(zhí)行 callback 方法,跳過第一個(gè)索引。如果提供initialValue,從索引0開始。在沒有初始值的空數(shù)組上調(diào)用 reduce 將報(bào)錯(cuò)
--摘自MDN
“[3,2,1].reduce(Math.pow)”拆分后等價(jià)于:
Math.pow(3, 1) // 3
Math.pow(3, 2) // 9
Math.pow(9, 1) // 9
對(duì)于“[].reduce(Math.pow)”,因?yàn)槭褂每諗?shù)組調(diào)用reduce,所以報(bào)錯(cuò)。
答案:error
第四題
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
考察知識(shí)點(diǎn):
1.運(yùn)算符的優(yōu)先級(jí)
2.“+”運(yùn)算符的隱式轉(zhuǎn)換
3.字符串的隱式轉(zhuǎn)換
解析:“+”運(yùn)算符的優(yōu)先級(jí)大于三目運(yùn)算符的優(yōu)先級(jí),所以執(zhí)行過程如下:
val === 'smtg' // true
'Value is ' + true // "Value is true"
"Value is true" ? 'Something' : 'Nothing' // "Something"
答案:"Something"
第五題
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
考察知識(shí)點(diǎn):
1.塊級(jí)作用域與變量提升
解析:使用var聲明的變量沒有塊級(jí)作用域(注意區(qū)分塊級(jí)作用域與函數(shù)作用域),使用let、const聲明的變量有塊級(jí)作用域,因?yàn)楹瘮?shù)內(nèi)的name用var聲明,所以不存在塊級(jí)作用域,導(dǎo)致變量提升。等價(jià)寫法如下:
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
答案:"Goodbye Jack"
第六題
var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
考查知識(shí)點(diǎn):
1.JS中能存儲(chǔ)的最大值

64位浮點(diǎn)數(shù)能表示的整數(shù)范圍是-2^53~2^53,即最大值為Math.pow(2, 53),當(dāng)超出這個(gè)值時(shí),自動(dòng)轉(zhuǎn)為最大值。因此本題的for循環(huán)會(huì)一直執(zhí)行下去,console.log語句永遠(yuǎn)無法執(zhí)行。
答案:死循環(huán)
第七題
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
考察知識(shí)點(diǎn):
1.數(shù)組跳index賦值
2.filter
解析:ary初始化為3個(gè)元素后賦值ary[10],數(shù)組中index從3到9的元素是未定義的,不會(huì)被自動(dòng)填充為undefined,而filter函數(shù)的callback不會(huì)回調(diào)數(shù)組中的未定義元素。驗(yàn)證如下:

注意區(qū)分未定義元素與undefined元素,filter函數(shù)的callback是會(huì)回調(diào)數(shù)組中undefined元素的,驗(yàn)證如下:

因?yàn)楸绢}中的數(shù)組不存在undefined元素,所以不存在通過條件篩選的元素,最終返回[]。
答案:[]
第八題
var two = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two]
考查知識(shí)點(diǎn):
1.浮點(diǎn)數(shù)的精度及表達(dá)方式
解析:這題比較簡(jiǎn)單,但為了說明原因還是啰嗦一下。老司機(jī)都知道,浮點(diǎn)數(shù)的運(yùn)算會(huì)存在精度缺失問題,為什么會(huì)精度缺失?我們知道計(jì)算機(jī)中的數(shù)值是以二進(jìn)制方式存儲(chǔ)的,以3.14的小數(shù)部分0.14為例,轉(zhuǎn)換如下:
// 0.14
0.14 * 2 = 0.28 0
0.28 * 2 = 0.56 0
0.56 * 2 = 1.12 1
0.12 * 2 = 0.24 0
0.24 * 2 = 0.28 0
0.48 * 2 = 0.96 0
0.96 * 2 = 1.92 1
0.92 * 2 = 1.84 1
...
所以0.14取8位二進(jìn)制有效數(shù)字的表達(dá)式為0.00100011,顯然當(dāng)小數(shù)部分轉(zhuǎn)成二進(jìn)制時(shí),可能存在有效數(shù)字范圍內(nèi)無法精確表達(dá)的問題,因此浮點(diǎn)數(shù)運(yùn)算會(huì)存在精度缺失問題。
回到本題,來計(jì)算一下0.1、0.2、0.6、0.8轉(zhuǎn)成二進(jìn)制是怎樣表達(dá)的:
// 0.1
0.1 * 2 = 0.2 0
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 0
0.2 * 2 ...
可以看到,以0.1為源,在轉(zhuǎn)換的過程中出現(xiàn)了0.2、0.4、0.8、0.6,在0.6之后又出現(xiàn)了0.2。所以,本題的四個(gè)小數(shù)在轉(zhuǎn)成二進(jìn)制后都是循環(huán)小數(shù)。
// 0.2 - 0.1
0.2 => 0.001000100010...
0.1 => 0.000100010001...
0.2 - 0.1 => 0.000100010001...
0.1 => 0.000100010001...
顯然,0.2 - 0.1的二進(jìn)制運(yùn)算結(jié)果與0.1的二進(jìn)制表達(dá)方式相等
// 0.8 - 0.6
0.8 => 0.100010001000...
0.6 => 0.000100010001...
0.8 - 0.6 => 0.011101110111...
0.2 => 0.001000100010...
顯然,0.8 - 0.6的二進(jìn)制運(yùn)算結(jié)果大于0.2的二進(jìn)制表達(dá)方式
答案:[true, false]
第九題
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'));
考查知識(shí)點(diǎn):
1.字符串對(duì)象與原生字符串的區(qū)別
解析:這一題也很簡(jiǎn)單,通過String構(gòu)造函數(shù)生成的字符串對(duì)象與原生字符串不相等,驗(yàn)證如下

- 原生字符串類型為"string",字符串對(duì)象類型為"object"
- 通過new調(diào)用String構(gòu)造函數(shù)生成的是字符串對(duì)象,直接調(diào)用String構(gòu)造函數(shù)生成的是原生字符串
除此之外,字符串對(duì)象與原生字符串在作為eval參數(shù)時(shí),表現(xiàn)也是不同的:

- 當(dāng)參數(shù)為原生字符串時(shí),eval會(huì)將字符串當(dāng)做源代碼處理
- 當(dāng)參數(shù)為字符串對(duì)象時(shí),eval會(huì)將字符串當(dāng)做對(duì)象處理
除此之外,在代碼里可以通過原生字符串訪問到字符串對(duì)象的方法,這是因?yàn)镴S在解析時(shí)會(huì)自動(dòng)將原生字符串轉(zhuǎn)換為字符串對(duì)象。
答案:"Do not know!"
第十題
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'));
考察知識(shí)點(diǎn):
1.字符串對(duì)象與原生字符串的區(qū)別
解析:見第九題
答案:"Case A"
第十一題
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);
考察知識(shí)點(diǎn):
1.字符串轉(zhuǎn)數(shù)字
2.Infinity
3.map
解析:本題關(guān)鍵點(diǎn)在于Infinity。Infinity表示正無窮大,所以任何數(shù)乘以無窮大仍為無窮大,任何數(shù)(除Infinity與-Infinity外)除以無窮大都為0。因?yàn)镮nfinity無法用準(zhǔn)確的數(shù)值來表示,所以Infinity除以Infinity的值為NaN。同理,因?yàn)闊o法準(zhǔn)確表示Infinity的值,所以“Infinity % 2”的值也為NaN。另外:
-9 % 2 // -1
答案:[true, true, true, false, false]
第十二題
parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
考察知識(shí)點(diǎn):
1.parseInt
解析:見第一題
答案:3, NaN, 3
第十三題
Array.isArray( Array.prototype )
考察知識(shí)點(diǎn):
1.Array.isArray
2.Array.prototype
解析:Array.isArray用來檢測(cè)對(duì)象是否為數(shù)組,Array.prototype也是數(shù)組,所以本題返回true。
對(duì)比instanceof,“Array.prototype instanceof Array”返回false。因?yàn)閕nstanceof的檢測(cè)機(jī)制為,檢測(cè)構(gòu)造函數(shù)的prototype是否在對(duì)象的原型鏈上。此時(shí)構(gòu)造函數(shù)的prototype就是要檢測(cè)的對(duì)象本身,顯然不在檢測(cè)對(duì)象的原型鏈上,所以返回false。再來做個(gè)試驗(yàn):

只要將obj的原型指向Array.prototype,此時(shí)instanceof就返回true。那么如何檢測(cè)對(duì)象的真是類型呢?typeof顯然不行,他對(duì)于實(shí)例對(duì)象都返回'object'。我們可以通過toString的默認(rèn)實(shí)現(xiàn)來獲取當(dāng)前對(duì)象的類型標(biāo)識(shí):
function objectType(obj) {
return Object.prototype.toString.apply(obj)
}

顯然,只是將obj的__proto__屬性指向Array.prototype,由Object構(gòu)造函數(shù)創(chuàng)建的obj對(duì)象并不會(huì)變成數(shù)組,他的類型標(biāo)識(shí)仍然是'Object'。
答案:true
第十四題
var a = [0];
if ([0]) {
console.log(a == true);
} else {
console.log("wut");
}
考查知識(shí)點(diǎn):
1.if條件判斷的自動(dòng)轉(zhuǎn)換
解析:送分題?!癷f([0])”為真,“[0] == true”為假,所以輸出false
答案:false
第十五題
[]==[]
考察知識(shí)點(diǎn):
1.==
2.深拷貝與淺拷貝
解析:先簡(jiǎn)單說說深拷貝與淺拷貝

深拷貝可以理解為重新創(chuàng)建一個(gè)與原對(duì)象各數(shù)據(jù)相同的對(duì)象,淺拷貝可以理解為引用。上圖中,a0、a1互為深拷貝關(guān)系,a1、a2互為淺拷貝關(guān)系。因?yàn)樯羁截悶橹匦聞?chuàng)建對(duì)象,所以a0和a1是不等的。而淺拷貝為引用關(guān)系,所以a1和a2是相等的。
回到本題,“[] == []”顯然等號(hào)兩端為兩個(gè)同類型,但不同的對(duì)象,所以返回false。
對(duì)于“==”而言,當(dāng)發(fā)現(xiàn)等號(hào)兩端數(shù)據(jù)類型不一致時(shí)會(huì)進(jìn)行隱式轉(zhuǎn)換,可以看一下這道題的變種:
[] == ![] // true
“!”運(yùn)算符優(yōu)先級(jí)高于“==”優(yōu)先級(jí),所以先計(jì)算“![]”,此時(shí)“![]”的值為false,所以相當(dāng)于比較[]與false是否相等。因?yàn)榈忍?hào)兩端數(shù)據(jù)類型不一致,此時(shí)進(jìn)行隱式轉(zhuǎn)換。[]為空數(shù)組轉(zhuǎn)換成0,false轉(zhuǎn)換成0,所以此時(shí)兩者是相等的。
答案:false
第十六題
'5' + 3
'5' - 3
考察知識(shí)點(diǎn):
1.加法運(yùn)算符
2.減法運(yùn)算符
解析:送分題,直接給答案。
答案:"53",2
第十七題
1 + - + + + - + 1
考察知識(shí)點(diǎn):
1.加減運(yùn)算符的疊加使用
解析:這道題很偏很爛,就跟當(dāng)年C語言用“++”操作符出的爛題一樣,沒啥實(shí)際意義

可以看到,JS中允許加減運(yùn)算符連接使用,但是不允許出現(xiàn)兩個(gè)加或者兩個(gè)減。當(dāng)加減運(yùn)算符連接使用時(shí),和單獨(dú)使用減法運(yùn)算符是一致的。

但是,當(dāng)加減運(yùn)算符中間出現(xiàn)空格時(shí),此時(shí)兩個(gè)加號(hào)與兩個(gè)減號(hào)是可以出現(xiàn)的,運(yùn)算規(guī)則相當(dāng)于“正正得正,負(fù)負(fù)得正,正負(fù)得正,負(fù)正得正”。
回到本題,可拆解為如下運(yùn)算:
1 + (- + + + - + 1)
括號(hào)內(nèi)運(yùn)算結(jié)果為1,所以本題等價(jià)于計(jì)算1+1的值,結(jié)果為2
答案:2
第十八題
var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return '1'; });
考查知識(shí)點(diǎn):
1.數(shù)組中的未定義元素是否會(huì)觸發(fā)map函數(shù)的callback
解析:在“第七題”的知識(shí)點(diǎn)filter中,我們說過,數(shù)組中的undefined元素會(huì)觸發(fā)filter函數(shù)的callback,而未定義元素不會(huì)觸發(fā),這個(gè)結(jié)論同樣適用于map及reduce。reduce相對(duì)于map和filter在這一點(diǎn)又略有不同,map和filter可以通過空數(shù)組調(diào)用,而reduce通過空數(shù)組調(diào)用會(huì)引發(fā)error,reduce空數(shù)組調(diào)用引發(fā)error的這個(gè)特性在“第三題”中也提到過。
知識(shí)都是想通的,在后面的解題中會(huì)越來越多提到前面的知識(shí)。
回到本題,首先生成一個(gè)長(zhǎng)度為3的空數(shù)組ary,然后將這個(gè)ary的第0個(gè)元素設(shè)置為2,此時(shí)ary的后兩個(gè)元素都是未定義的,通過ary調(diào)用map。所以返回的新數(shù)組第0個(gè)元素為"1",后兩個(gè)仍為未定義元素。

答案:長(zhǎng)度為3,第0個(gè)元素為"1",后兩個(gè)元素未定義的數(shù)組(再次強(qiáng)調(diào),注意區(qū)分未定義與undefined)
第十九題
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c) {
c = 10
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1)
考查知識(shí)點(diǎn):
1.arguments
解析:本題相對(duì)來說難度不大,但是如果配合默認(rèn)參數(shù)和剩余參數(shù)就有點(diǎn)繞了,先說本題解法。在沒有默認(rèn)參數(shù)及剩余參數(shù)的正常模式(非嚴(yán)格模式)下,函數(shù)的參數(shù)與arguments是互通的,所以在bar函數(shù)中將c賦值為10,arguments[2]也變成了10,再調(diào)用sidEffecting函數(shù)將arguments[0]賦值為10,所以a也變成了10。最終a、b、c對(duì)應(yīng)的值為10、1、10,所以最終結(jié)果為21。
拓展:
1.默認(rèn)參數(shù)
2.剩余參數(shù)
下面說有默認(rèn)參數(shù)的情況:
function argumentsTest(a = 1) {
console.log(a)
console.log(arguments[0])
a = 2
console.log(arguments[0])
arguments[0] = 3
console.log(a)
}


可見,只要函數(shù)表達(dá)式中存在默認(rèn)參數(shù),不管在調(diào)用的時(shí)候是否給函數(shù)的默認(rèn)參數(shù)傳值,這個(gè)參數(shù)和對(duì)應(yīng)arguments中的數(shù)據(jù)都是分離的。但是注意,這兩種調(diào)用方式有一點(diǎn)不同:當(dāng)調(diào)用時(shí)不給a賦值,取出來的arguments[0]是undefined。
如果函數(shù)表達(dá)式是這樣的:
function argumentsTest(a = 1, b)
b與arguments[1]也是分離的,這里不再贅述。
接著說剩余參數(shù)的情況:
function argumentsTest(a, ...theArgs) {
console.log(a)
console.log(arguments[0])
a = 2
console.log(arguments[0])
arguments[0] = 3
console.log(a)
}

顯然,含剩余參數(shù)的函數(shù),參數(shù)與arguments中對(duì)應(yīng)的元素也不互通。
答案:21
第二十題
var a = 111111111111111110000,
b = 1111;
a + b;
考查知識(shí)點(diǎn):
- JS中能存儲(chǔ)的最大值
解析:本題考點(diǎn)與第六題相同,a的值111111111111111110000已超出64未浮點(diǎn)數(shù)能表示的最大整數(shù),因此a + b的結(jié)果仍為a
答案:111111111111111110000
第二十一題
var x = [].reverse;
x();
考察知識(shí)點(diǎn):
1.reverse
解析:本題用變量x接收reverse函數(shù),然后調(diào)用“x()”,由于reverse是屬于Array.prototype,所以應(yīng)該由數(shù)組來調(diào)用,而當(dāng)前調(diào)用方式相當(dāng)于通過全局作用域中的this調(diào)用(即window),但是window本身不是數(shù)組,所以返回error。筆者在Chrome和Firefox中驗(yàn)證了一下,確實(shí)返回error。
不過原網(wǎng)站給的答案是window,而非error。解釋是,此時(shí)reverse函數(shù)(即x)中的this指向window,所以返回window。介于出題時(shí)間很早,猜測(cè)那個(gè)時(shí)候的reverse函數(shù)沒有做類型校驗(yàn)?
答案:error
第二十二題
Number.MIN_VALUE > 0
考察知識(shí)點(diǎn):
1.Number.MIN_VALUE
解析:送分題。Number.MIN_VALUE為在JS中能表示的最小正數(shù),值約為 5e-324,很接近于+0,但仍然大于0。
答案:true
第二十三題
[1 < 2 < 3, 3 < 2 < 1]
考察知識(shí)點(diǎn):
1.<
解析:送分題。<運(yùn)算符得到的結(jié)果為true或者false(當(dāng)然,>運(yùn)算符也一樣),因此本題可拆解為如下步驟:
// 1 < 2 < 3
true < 3
1 < 3
true
// 3 < 2 < 1
false < 1
0 < 1
true
答案:[true, true]
第二十四題
// the most classic wtf
2 == [[[2]]]
考察知識(shí)點(diǎn):
1.==
解析:送分題。==運(yùn)算符在等號(hào)兩邊類型不一致時(shí)會(huì)進(jìn)行隱式轉(zhuǎn)換,直到轉(zhuǎn)換為相同類型再做比較。數(shù)值類型與引用類型進(jìn)行比較,會(huì)將引用類型轉(zhuǎn)換成數(shù)值類型。因此等號(hào)右邊最終被轉(zhuǎn)換為2,此時(shí)相當(dāng)于:
// 2 == [[[2]]]
2 == [[2]]
2 == [2]
2 == 2
答案:true
第二十五題
3.toString()
3..toString()
3...toString()
考察知識(shí)點(diǎn):
1.浮點(diǎn)數(shù)的表示方法
解析:這一題也不難,先看代碼

因此,.3與3.都是合法的表達(dá)方式,此時(shí)相當(dāng)于省略了數(shù)字0?;氐奖绢},可做以下等價(jià)寫法:
// 3.toString()
3.0toString()
// 3..toString()
3.0.toString()
// 3...toString()
3.0..toString()
顯然,一和三語法錯(cuò)誤,二可以解析。因此,最終答案為error, "3.0", error。
答案:error, "3.0", error
第二十六題
(function(){
var x = y = 1;
})();
console.log(y);
console.log(x);
考察知識(shí)點(diǎn):
1.作用域
解析:本題其實(shí)也很簡(jiǎn)單,來看等價(jià)寫法
// (function() { var x = y = 1})()
(function() {
y = 1
var x = y
)()
顯然,x的作用域僅在匿名函數(shù)內(nèi)部,而y的作用域?yàn)槿?。因此,第一句log輸出為1,而第二句log輸出error。
答案:1, error
第二十七題
var a = /123/,
b = /123/;
a == b
a === b
考察知識(shí)點(diǎn):
1.==
2.===
解析:送分題。正則是對(duì)象,對(duì)于==,等號(hào)兩邊類型相等,但不是指向同一個(gè)對(duì)象,所以返回false。對(duì)于===,直接比較等號(hào)兩端是否指向同一個(gè)地址,由于是不同對(duì)象,所以地址不同,返回false。
答案:false, false
第二十八題
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4]
a == b
a === b
a > c
a < c
考察知識(shí)點(diǎn):
1.==
2.===
3.>、<
解析:送分題。本題前兩個(gè)答案都為false,解釋同第二十七題。當(dāng)大于和小于運(yùn)算符兩端都為數(shù)組時(shí),逐個(gè)取出進(jìn)行比較,因此后兩個(gè)答案分別為false和true。
答案:false, false, false, true
第二十九題
var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]
考察知識(shí)點(diǎn):
1.函數(shù)的prototype屬性
2.對(duì)象的__proto__屬性
3.大括號(hào)生成的對(duì)象的constructor
解析:送分題。a為實(shí)例對(duì)象,在為自定義prototype屬性前,prototype的值為undefined。因此,“a.prtotype == b”返回false。對(duì)于用大括號(hào)生成的對(duì)象,實(shí)際是調(diào)用Object構(gòu)造函數(shù)創(chuàng)建的。而“Object.getPrototypeOf(a)”訪問的屬性是對(duì)象的__proto__,實(shí)例對(duì)象的__proto__指向?qū)ο髽?gòu)造函數(shù)的prtotype屬性,而a對(duì)象的構(gòu)造函數(shù)就是Object,因此“Object.getPrototypeOf(a) === b”返回true。
答案:[false, true]
第三十題
function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b
考察知識(shí)點(diǎn):
1.prototype與__proto__的區(qū)別
解析:送分題。在未手動(dòng)指定兩個(gè)屬性指向的情況下,兩者顯然是不等的。(詳細(xì)解讀可以參考筆者之前寫的《大話JavaScript對(duì)象》這篇文章)
答案:false
第三十一題
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]
考查知識(shí)點(diǎn):
1.configurable與writable
解析:送分題。我們知道每個(gè)函數(shù)內(nèi)部都有name屬性,這個(gè)屬性是只讀的,顯然他來自函數(shù)的原型鏈上。并且name屬性的configurable為true,而writable為false,因此foo.name可以賦值,但是并不會(huì)改變name中存儲(chǔ)的值。
答案:["foo", "foo"]
第三十二題
"1 2 3".replace(/\d/g, parseInt)
考察知識(shí)點(diǎn):
1.replace
解析:當(dāng)replace函數(shù)的第二個(gè)參數(shù)為函數(shù)時(shí),對(duì)應(yīng)的函數(shù)參數(shù)為match、p1,p2, ...、offset、string。所以可分解為如下表達(dá)方式:
parseInt('1', 0)
parseInt('2', 2) //2進(jìn)制中不存在2
parseInt('3', 4)
因此答案為,"1 NaN 3"。
答案:"1 NaN 3"
第三十三題
function f() {}
var parent = Object.getPrototypeOf(f);
f.name // ?
parent.name // ?
typeof eval(f.name) // ?
typeof eval(parent.name) // ?
考察知識(shí)點(diǎn):
1.typeof
2.eval
解析:f.name顯然返回函數(shù)名"f",parent為f對(duì)象的原型,是個(gè)匿名函數(shù),因此parent.name返回""。對(duì)于后兩句,等價(jià)于如下寫法:
// typeof eval(f.name)
typeof eval("foo")
typeof foo
// typeof eval(parent.name)
typeof eval("")
typeof undefined
因?yàn)閒oo已定義為函數(shù)所以等價(jià)后的第一句返回function,第二句仍返回undefined。
原題給的最后一個(gè)答案為error,但經(jīng)Chrome和Firefox驗(yàn)證后確實(shí)返回undefined,猜測(cè)老版本的"typeof undefined"不符合語義所以拋錯(cuò)?
答案:"f", "", function, undefined
第三十四題
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]
考察知識(shí)點(diǎn):
1.test函數(shù)的隱式轉(zhuǎn)換
解析:送分題。正則對(duì)象的test函數(shù)對(duì)應(yīng)參數(shù)為字符串,如果傳入的不是字符串,會(huì)通過toString函數(shù)進(jìn)行轉(zhuǎn)換,如果傳入的參數(shù)不含有toString方法,這里又分為兩種情況:
1.不是繼承自O(shè)bject.prototype的對(duì)象,如Object.create(null)
2.null、undefined
對(duì)于不是繼承自O(shè)bject.prototype的對(duì)象會(huì)因?yàn)檎也坏絫oString方法而報(bào)錯(cuò),對(duì)于null、undefined會(huì)直接轉(zhuǎn)換成對(duì)于的字符串"null"、"undefined"。
回到本題,等價(jià)寫法如下:
// lowerCaseOnly.test(null)
lowerCaseOnly.test("null")
// lowerCaseOnly.test()
lowCaseOnly.test("undefined")
答案:[true, true]
第三十五題
[,,,].join(", ")
考察知識(shí)點(diǎn):
1.數(shù)組中的逗號(hào)
解析:JS中的數(shù)組允許最后一個(gè)元素后跟上,

顯然,這里是3個(gè)未定義元素,而非4個(gè)。因此,最終結(jié)果為:", , "(注意,join函數(shù)內(nèi)部的字符串中逗號(hào)后面還有個(gè)空格)
答案:", , "
第三十六題
var a = {class: "Animal", name: 'Fido'};
a.class
考察知識(shí)點(diǎn):
1.關(guān)鍵字作為屬性名稱
解析:這完全取決于JS引擎是否支持關(guān)鍵字作為屬性名,若支持則返回"Animal",不支持則報(bào)錯(cuò)。經(jīng)測(cè)試,Chrome和Firefox均返回"Animal"。
答案:"Animal"
第三十七題
var a = new Date("epoch")
考察知識(shí)點(diǎn):
1.Date傳參
解析:顯然Date構(gòu)造函數(shù)的參數(shù)不合法,所以返回一個(gè)不合法的Date實(shí)例對(duì)象,即Invalid Date。
答案:Invalid Date
第三十八題
var a = Function.length,
b = new Function().length
a === b
考查知識(shí)點(diǎn):
1.Function的length
2.函數(shù)的length
解析:Function.length定義為1,而函數(shù)的length屬性是形參的個(gè)數(shù),這其中不包括剩余參數(shù),如果參數(shù)中存在默認(rèn)值,則length的長(zhǎng)度為第一個(gè)含默認(rèn)參數(shù)之前參數(shù)的個(gè)數(shù)。因?yàn)椤皀ew Function().length”沒有形參,所以b的值為0,而a的值為1。因此,最終結(jié)果為false。
答案:false
第三十九題
var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c]
考察知識(shí)點(diǎn):
1.Date作為構(gòu)造函數(shù)與普通函數(shù)調(diào)用時(shí)的返回值
2.===
解析:Date作為普通函數(shù)調(diào)用返回字符串,作為構(gòu)造函數(shù)調(diào)用返回Date實(shí)例對(duì)象。因此a為字符串類型,b和c為Date類型,但是b和c為兩個(gè)不同的實(shí)例對(duì)象,指向的地址不同,因此“b===c”返回false。其他兩個(gè)等號(hào)左右兩邊的數(shù)據(jù)類型都不相同,自然都返回false。
答案:[false, false, false]
第四十題
var min = Math.min(), max = Math.max()
min < max
考察知識(shí)點(diǎn):
1.Math.min
2.Math.max
解析:min返回一組數(shù)據(jù)中的最小值,max返回一組數(shù)據(jù)中的最大值。但是本題min和max都沒有參數(shù),在這種情況下,min返回+Infinity,而max返回-Infinity。因此,“min < max”返回false。
答案:false
第四十一題
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");
[a1 === a2, a3 === a4]
考察知識(shí)點(diǎn):
1.exec
解析:當(dāng)正則表達(dá)式使用“g”標(biāo)記時(shí),執(zhí)行exec時(shí),查找的起點(diǎn)為上一次執(zhí)行exec后lastIndex的值。而正則表達(dá)式中不帶“g”標(biāo)記的,每次重頭查找。因?yàn)閚umRe中帶“g”,所以a1的值為1, a3的值為null。而wordRe中不帶“g”,所以a2的值為1,a4的值為2。
答案:[true, false]
第四十二題
var a = new Date("2014-03-19"),
b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()]
考察知識(shí)點(diǎn):
- Date
解析:這題有點(diǎn)讓人難受,因?yàn)榈谝环N寫法表達(dá)的是2014年3月19號(hào)星期三,而第二種寫法表達(dá)的是2014年4月19號(hào)星期六。在getDay這塊也有坑,getDay返回的是星期幾,因此“a.getDay()”的值為3,而“b.getDay()”的值為6,如果想獲取Date實(shí)例對(duì)象當(dāng)前月份的天數(shù),要使用date函數(shù)。再說說getMonth的坑,getMonth確實(shí)是獲取月份的,但是month是從0開始數(shù)的,所以獲取到的月份會(huì)比實(shí)際月份少一個(gè)月。就本題而言,a.getMonth()獲取到的月份是2,b.getMonth獲取到的月份是3。
真難受。。
答案:[false, false]
第四十三題
if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
'a gif file'
} else {
'not a gif file'
}
考察知識(shí)點(diǎn):
1.String.prototype.match()
解析:這題比較簡(jiǎn)單。如果match中傳入的obj不是正則表達(dá)式對(duì)象,JS會(huì)通過 new RegExp(obj) 將其轉(zhuǎn)換成一個(gè)正則表達(dá)式對(duì)象。如果match參數(shù)為空,則直接返回[""]?;氐奖绢},match內(nèi)的參數(shù).gif就被轉(zhuǎn)換成/.gif/,因此可以匹配。
答案:"a gif file"
第四十四題
function foo(a) {
var a;
return a;
}
function bar(a) {
var a = 'bye';
return a;
}
[foo('hello'), bar('hello')]
考察知識(shí)點(diǎn):
1.函數(shù)內(nèi)部變量的聲明
解析:送分題。直接上等價(jià)寫法:
function foo(a) {
a;
return a
}
function bar(a) {
a = 'bye';
return a;
}
答案:["hello", "bye"]
JavaScript真讓人頭大??
Have fun!