44道JavaScript難題(JavaScriptPuzzlers!)

李博bluemind發表於2018-06-03

這是一套很經典的JavaScript題了,做之前一些題我也覺得稀奇古怪,但一道一道做,記下錯題,去查解釋,做完感覺真的很值得,有點像回到高中時候,就想到了沙耶加。如果在學習路上疲憊了,安利你們《墊底辣妹》
。這裡是這套題的原文連結,可以不看答案一道一道去做。

下一篇《一站到底 —前端基礎之網路》

1. [“1”, “2”, “3”].map(parseInt)

答案:[1, NaN, NaN]
解析:parseInt (val, radix) :兩個引數,val值,radix基數(就是多少進位制轉換)
 map 能傳進回撥函式 3引數 (element, index, array)
 parseInt(`1`, 0); //0代表10進位制
 parseInt(`2`, 1); //沒有1進位制,不合法
 parseInt(`3`, 2); //2進位制根本不會有3
鞏固:["1", "1", "11","5"].map(parseInt) //[1, NaN, 3, NaN]
 parseInt(`13`,2) // 1 ,
 計算機在二進位制只認識0,1,parseInt轉換時就當作不認識的字元忽略了
 parseInt(`18str`) //18 10進位制能認識到9
 parseInt(1/0,19) // 18 
 1/0 == Infinity 19 進位制計算機能認識最後一個字元是i 
 詳細解析在下面的連結
複製程式碼

鞏固題詳細解釋在stackoverflow

2. [typeof null, null instanceof Object]

答案:["object", false]
解析:null代表空物件指標,所以typeof判斷成一個物件。可以說JS設計上的一個BUG
 instanceof 實際上判斷的是物件上建構函式,null是空當然不可能有建構函式
鞏固:null == undefined //true null === undefined //flase
複製程式碼

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

答案:an error
解析:Math.pow (x , y) x 的 y 次冪的值
 reduce(fn,total)
 fn (total, currentValue, currentIndex, arr) 
 如果一個函式不傳初始值,陣列第一個組預設為初始值.
 [3,2,1].reduce(Math.pow)
 Math.pow(3,2) //9
 Math.pow(9,1) //9
鞏固:[].reduce(Math.pow) //空陣列會報TypeError
 [1].reduce(Math.pow) //只有初始值就不會執行回撥函式,直接返回1
 [].reduce(Math.pow,1) //只有初始值就不會執行回撥函式,直接返回1
 [2].reduce(Math.pow,3) //傳入初始值,執行回撥函式,返回9
複製程式碼

4.

 var val = `smtg`;
 console.log(`Value is ` + (val === `smtg`) ? `Something` : `Nothing`);
複製程式碼

這段程式碼的執行結果?

答案:Something
解析:字串連線比三元運算有更高的優先順序 
 所以原題等價於 `Value is true` ? `Somthing` : `Nonthing` 
 而不是 `Value is` + (true ? `Something` : `Nonthing`)
鞏固:
 1 || fn() && fn() //1 
 1 || 1 ? 2 : 3 ; //2 
 鞏固的解釋請看下面這篇文章
複製程式碼

Like Sunday, Like Rain – JavaScript運算子優先順序之謎

5.

var name = `World!`;
(function () {
if (typeof name === `undefined`) {
 var name = `Jack`;
 console.log(`Goodbye ` + name);
} else {
 console.log(`Hello ` + name);
}
})();
複製程式碼

這段程式碼的執行結果?

答案:Goodbye Jack
解析:(1)typeof時 name變數提升。 在函式內部之宣告未定義
 (2)typeof優先順序高於===
鞏固:
 var str = `World!`; 
 (function (name) {
 if (typeof name === `undefined`) {
 var name = `Jack`;
 console.log(`Goodbye ` + name);
 } else {
 console.log(`Hello ` + name);
 }
 })(str);
 答案:Hello World 因為name已經變成函式內區域性變數
複製程式碼

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);
複製程式碼

這段程式碼的執行結果?

答案:other ,不是101
解析:js中可以表示的最大整數不是2的53次方,而是1.7976931348623157e+308。2的53次方不是js能表示的最大整數而應該是能正確計算且不失精度的最大整數,
鞏固:
 var END = 1234567635;
 var START = END - 1024;
 var c = count = 0;
 for (var i = START; i <= END; i++) {
 c = count++;
 }
 console.log(count); //1025
 console.log(c); //1024
複製程式碼

7.

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

這段程式碼的執行結果?

答案:[]
解析:filter() 不會對空陣列進行檢測。會跳過那些空元素
鞏固:
 var ary = [0,1,2,undefined,undefined,undefined,null];
 ary.filter(function(x) { return x === undefined;});
 // [undefined, undefined, undefined] 
複製程式碼

8.

var two = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two]
複製程式碼

這段程式碼的執行結果?

答案:[true, false]
解析:IEEE 754標準中的浮點數並不能精確地表達小數
鞏固:var two = 0.2;
 var one = 0.1;
 var eight = 0.8;
 var six = 0.6;
 ( eight - six ).toFixed(4) == two 
 //true
複製程式碼

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`));
複製程式碼

這段程式碼的執行結果?

答案:Do not know!
解析:switch判斷的是全等(===) ,new String(x)是個物件
鞏固:var a = new String(`A`) ;
 a.__proto__
 // String.prototype 例項的原型指向建構函式的原型物件
複製程式碼

10.

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`));
複製程式碼

這段程式碼的執行結果?

答案:Case A
解析:String(`A`)就是返回一個字串
鞏固: var a2 = `A`;
 a2.__proto__ // String.prototype 
 a1.__proto__ === a2.__proto__ // true 上一題的a.__proto__
 那字串不是物件為啥也指向String.prototype?
解析:b是基本型別的值,邏輯上不應該有原型和方法。為了便於操作,有一種特殊的引用型別(基本包裝型別)String。其實讀取時,後臺會自動完成下面的操作:
 var str = new String("A"); //建立例項
 str.__proto__; //呼叫指定屬性和方法
 str = null; //銷燬例項
 所以 a1.__proto__ === a2.__proto__
 但注意基本包裝型別特殊就在於它物件(str)的生命週期,只存在於一行程式碼(a1.__proto__ === a2.__proto__)的執行瞬間。
 這也就解釋了為啥字串也能操作屬性和方法但不能新增。基本包裝型別有三個(String,Number,Boolean)
 (詳情請看《js高程》 5.6基本包型別 P119)
複製程式碼

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);
複製程式碼

這段程式碼的執行結果?

答案:[true, true, true, false, false]
解析:%如果不是數值會呼叫Number()去轉化
 `13` % 2 // 1
 Infinity % 2 //NaN Infinity 是無窮大
 -9 % 2 // -1
鞏固: 9 % -2 // 1 餘數的正負號隨第一個運算元
複製程式碼

12.

parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
複製程式碼

這段程式碼的執行結果?

答案:3 NaN 3
解析:2進位制不可能有3
複製程式碼

13.

Array.isArray( Array.prototype )
複製程式碼

這段程式碼的執行結果?

答案:true
解析:Array.prototype是一個陣列
 陣列的原型是陣列,物件的原型是物件,函式的原型是函式
複製程式碼

14.

var a = [0];
if ([0]) {
 console.log(a == true);
} else {
 console.log("wut");
}
複製程式碼

這段程式碼的執行結果?

答案:false
解析:[0]的boolean值是true
 console.log(a == true); // 轉換為數字進行比較, a轉換先toString,轉化成`0`,再Number(`0`) 轉化成數值0
 Number(true) => 1 ,所有是false
複製程式碼

15.[]==[]

答案:false
解析:兩個引用型別, ==比較的是引用地址
鞏固:[]== ![] 
 (1)! 的優先順序高於== ,右邊Boolean([])是true,取返等於 false
 (2)一個引用型別和一個值去比較 把引用型別轉化成值型別,左邊0
 (3)所以 0 == false 答案是true
複製程式碼

16.

`5` + 3
`5` - 3
複製程式碼

這段程式碼的執行結果?

答案:53 2
解析:加號有拼接功能,減號就是邏輯運算
鞏固:typeof (+"1") // "number" 對非數值+—常被用來做型別轉換相當於Number()
複製程式碼

17. 1 + – + + + – + 1

答案:2
解析:+-又是一元加和減操作符號,就是數學裡的正負號。負負得正哈。 
鞏固: 一元運算子還有一個常用的用法就是將自執行函式的function從函式宣告變成表示式。
 常用的有 + - ~ ! void
 + function () { }
 - function () { }
 ~ function () { }
 void function () { }
複製程式碼

18.

var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return `1`; });
複製程式碼

這段程式碼的執行結果?

答案:["1", empty × 2]
解析:如過沒有值,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) 
複製程式碼

這段程式碼的執行結果?

答案:21, 
解析:arguments會和函式引數繫結。
鞏固:但如果es6付給初始值則無法修改,因為es6編譯後用了嚴格模式
 function sidEffecting(ary) {
 ary[0] = ary[2];
 }
 function bar(a=1,b,c) {
 c = 10
 sidEffecting(arguments);
 return a + b + c;
 }
 bar(1,1,1)
 //12
 這裡總結一下:嚴格模式和非嚴格模式
 (1)嚴格模式arguments物件是傳入函式內實參列表的靜態副本;非嚴格模式下,指向同一個值的引用 
 (2)嚴格模式變數必須先宣告,才能使用
 (3)嚴格模式中 call apply傳入null undefined保持原樣不被轉換為window
複製程式碼

20.

var a = 111111111111111110000,
b = 1111;
a + b;
複製程式碼

這段程式碼的執行結果?

答案:11111111111111111000
解析:在JavaScript中number型別在JavaScript中以64位(8byte)來儲存。這64位中有符號位1位、指數位11位、實數位52位。2的53次方時,是最大值。其值為:9007199254740992(0x20000000000000)。超過這個值的話,運算的結果就會不對.
複製程式碼

21.

var x = [].reverse;
x();
複製程式碼

這段程式碼的執行結果?

答案:error
解析:原來答案是window
 x = [].reverse 是把reverse函式賦值給x
 reverse 函式處理的是呼叫它的this
 比如 [1,2,3].reverse()時,它的this是[1,2,3]
 以前reverse是非嚴格模式的函式下,沒傳this會預設為window
 現在的reverse使用嚴格模式編寫,應該是undefined,所以會報型別轉換錯誤
 更多解釋可以看下面"fe-baidu"的評論
複製程式碼

22.Number.MIN_VALUE > 0

答案:true
解析:MIN_VALUE 屬性是 JavaScript 中可表示的最小的數(接近 0 ,但不是負數)。它的近似值為 5 x 10-324。
複製程式碼

23.[1 < 2 < 3, 3 < 2 < 1]

答案:[true,true]
解析: 1 < 2 => true;
 true < 3 => 1 < 3 => true;
 
 3 < 2 => false;
 false < 1 => 0 < 1 => true;
複製程式碼

24. 2 == [[[2]]]

答案:true
解析:值和引用型別去比較,把引用型別轉話成值型別
 [[[2]]])//2
鞏固:++[[]][+[]]+[+[]] //"10"
 (1)(++([[]][+[]])) + [+[]] //運算子權重判斷,安利一下第四題下面的文章
 (2)(++([[]][0])) + [0] // 16題中我們講過+用來做型別轉換Number([]) ===0
 (3)+([] + 1) + [0] //[[]]陣列的第0項就是[],++代表自增+1
 ******* 注意這一步不是 (++[]) + [0] 這樣是錯誤的 **********
 (4)+([] + 1) + [0] // 前面+將"1"轉成數字1 後邊,+是拼接 "0" 所以是字串"10"
 這題的詳細解釋在下面的連結中。高票答案解釋的非常贊,推薦閱讀
複製程式碼
鞏固題詳細解釋在stackoverflow

25.

3.toString()
3..toString()
3...toString()
複製程式碼

這段程式碼的執行結果?

答案:error, "3", error
解析:因為在 js 中 1.1, 1., .1 都是合法的數字. 那麼在解析 3.toString 的時候這個 . 到底是屬於這個數字還是函式呼叫呢? 只能是數字, 因為3.合法啊!
複製程式碼

26.

(function(){
 var x = y = 1;
})();
console.log(y);
console.log(x);
複製程式碼

這段程式碼的執行結果?

答案:1, error
解析:y 被賦值成全域性變數,等價於
 y = 1 ;
 var x = y;
複製程式碼

27.

var a = /123/,
b = /123/;
a == b
a === b
複製程式碼

這段程式碼的執行結果?

答案:false, false
解析:正則是物件,引用型別,相等(==)和全等(===)都是比較引用地址
複製程式碼

28.

var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4]
a == b
a === b
a > c
a < c
複製程式碼

這段程式碼的執行結果?

答案:false, false, false, true
解析:相等(==)和全等(===)還是比較引用地址
 引用型別間比較大小是按照字典序比較,就是先比第一項誰大,相同再去比第二項。
複製程式碼

29.

var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b] 
複製程式碼

這段程式碼的執行結果?

答案:false, true
解析:Object 的例項是 a,a上並沒有prototype屬性
 a的__poroto__ 指向的是Object.prototype,也就是Object.getPrototypeOf(a)。a的原型物件是b
複製程式碼

30.

function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b 
複製程式碼

這段程式碼的執行結果?

答案:false
解析:a是建構函式f的原型 : {constructor: ƒ}
 b是例項f的原型物件 : ƒ () { [native code] }
複製程式碼

31.

function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name] 
複製程式碼

這段程式碼的執行結果?

答案:["foo", "foo"]
解析:函式的名字不可變.
複製程式碼

32.”1 2 3″.replace(/d/g, parseInt)

答案:"1 NaN 3"
解析:replace() 回撥函式的四個引數:
 1、匹配項 
 2、與模式中的子表示式匹配的字串 
 3、出現的位置 
 4、stringObject 本身 。
如果沒有與子表示式匹配的項,第二引數為出現的位置.所以第一個引數是匹配項,第二個引數是位置
 parseInt(`1`, 0)
 parseInt(`2`, 2) //2進位制中不可能有2
 parseInt(`3`, 4)
鞏固:
 "And the %1".replace(/%([1-8])/g,function(match,a , b ,d){
 console.log(match +" "+ a + " "+ b +" "+d )
 });
 //%1 1 8 And the %1 
複製程式碼

33.

function f() {}
var parent = Object.getPrototypeOf(f);
f.name // ?
parent.name // ?
typeof eval(f.name) // ?
typeof eval(parent.name) // ? 
複製程式碼

這段程式碼的執行結果?

答案:"f", "Empty", "function", error
解析:f的函式名就是f
 parent是f原型物件的名字為"" ,
 先計算eval(f.name) 為 f,f的資料型別是function
 eval(parent.name) 為undefined, "undefined"
複製程式碼

34.

var lowerCaseOnly = /^[a-z]+$/;
lowerCaseOnly.test(null), lowerCaseOnly.test()]
複製程式碼

這段程式碼的執行結果?

答案:[true, true]
解析:這裡 test 函式會將引數轉為字串. `nul`, `undefined` 自然都是全小寫了
複製程式碼

35.[,,,].join(“,”)

答案:",,"
解析:因為javascript 在定義陣列的時候允許最後一個元素後跟一個,
 所以這個陣列長度是3,
鞏固: [,,1,].join(".").length // 3 
複製程式碼

36.

var a = {class: "Animal", name: `Fido`};
a.class 
複製程式碼

這段程式碼的執行結果?

答案:other
解析:這取決於瀏覽器。類是一個保留字,但是它被Chrome、Firefox和Opera接受為屬性名。在另一方面,每個人都會接受大多數其他保留詞(int,私有,丟擲等)作為變數名,而類是VordBoint。
複製程式碼

37.var a = new Date(“epoch”)

答案:other
解析:您得到“無效日期”,這是一個實際的日期物件(一個日期的日期為true)。但無效。這是因為時間內部保持為一個數字,在這種情況下,它是NA。
 在chrome上是undefined 
 正確的是格式是var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
複製程式碼

38.

var a = Function.length,
b = new Function().length
a === b
複製程式碼

這段程式碼的執行結果?

答案:false
解析:首先new在函式帶()時運算優先順序和.一樣所以從左向右執行
 new Function() 的函式長度為0
鞏固:function fn () {
 var a = 1;
 }
 console.log(fn.length) 
 //0 fn和new Function()一樣
複製程式碼

39.

var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c]
複製程式碼

這段程式碼的執行結果?

答案:[false, false, false]
解析:當日期被作為建構函式呼叫時,它返回一個相對於劃時代的物件(JAN 01 1970)。當引數丟失時,它返回當前日期。當它作為函式呼叫時,它返回當前時間的字串表示形式。
a是字串 a === b // 資料型別都不同,肯定是false
b是物件 b === c // 引用型別,比的是引用地址
c也是物件 a === c // 資料型別都不同,肯定是false
鞏固: var a = Date(2018);
 var b = Date(2001);
 [a ===b ]
 //[true] Date() 方法獲得當日的日期,作為函式呼叫不需要,返回的同一個字串
 "Tue Jun 12 2018 14:36:24 GMT+0800 (CST)" 當然如果a,b執行時間相差1秒則為false
複製程式碼

40.

var min = Math.min(), max = Math.max()
min < max
複製程式碼

這段程式碼的執行結果?

答案:false
解析: Math.min 不傳引數返回 Infinity, Math.max 不傳引數返回 -Infinity ,Infinity應該大於-Infinity,所以是false
 
鞏固:Number.MAX_VALUE > Number.MIN_VALUE //true
複製程式碼

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");
[a1 === a2, a3 === a4]
複製程式碼

這段程式碼的執行結果?

答案:[true, false]
解析: /g有一個屬性叫lastIndex,每次匹配如果沒有匹配到,它將重置為0,如果匹配到了,他將記錄匹配的位置。我們看一個簡單的例子吧。
 var numRe = /num=(d)/g;
 numRe.test("num=1abcwewe") //true
 numRe.lastIndex //5 匹配到num=1後在5的索引位置
 numRe.exec("num=1") //fales 這次要從5的索引位置,開始匹配
 numRe.lastIndex //0 上一次匹配失敗了numRe.lastIndex重製為0
複製程式碼

42.

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

這段程式碼的執行結果?

答案:[false, false]
解析: var a = new Date("2014-03-19") //能夠識別這樣的字串,返回想要的日期
 Wed Mar 19 2014 08:00:00 GMT+0800 (CST)
 b = new Date(2014, 03, 19); //引數要按照索引來
 Sat Apr 19 2014 00:00:00 GMT+0800 (CST)
 月是從0索引,日期是從1 
 getDay()是獲取星期幾
 getMonth()是獲取月份所以都不同
鞏固: [a.getDate() === b.getDate()] //true
複製程式碼

43.

 if (`http://giftwrapped.com/picture.jpg`.match(`.gif`)) {
 `a gif file`
 } else {
 `not a gif file`
 }
複製程式碼

這段程式碼的執行結果?

答案:`a gif file`
解析: String.prototype.match 接受一個正則, 如果不是, 按照 new RegExp(obj) 轉化. 所以 . 並不會轉義 。 那麼 /gif 就匹配了 /.gif/
鞏固: if (`http://giftwrapped.com/picture.jpg`.indexOf(`.gif`)) {
 `a gif file`
 } else {
 `not a gif file`
 }
 //indexOf如果匹配不到返回是-1 所以是 `a gif file` 
複製程式碼

44.

function foo(a) {
 var a;
 return a;
}
function bar(a) {
 var a = `bye`;
 return a;
}
[foo(`hello`), bar(`hello`)]
複製程式碼

這段程式碼的執行結果?

答案:["hello", "bye"]
解析:最後一題很簡單吧,變數宣告
複製程式碼

原文釋出時間:2018-06-12

原文作者:17點

本文來源掘金如需轉載請緊急聯絡作者


相關文章