44個 Javascript題附帶解析

原題來自: javascript-puzzlersjavascript

你們能夠先去作一下感覺感覺. 當初個人成績是 19/44...java

第 1 題

["1", "2", "3"].map(parseInt);
複製代碼

首先, map接受兩個參數, 一個回調函數 callback, 一個回調函數的this值,其中回調函數接受三個參數 currentValue, index, array;而題目中, map 只傳入了回調函數--parseInt.其次, parseInt 只接受兩個兩個參數 string, radix(基數).正則表達式

  • 在沒有指定基數,或者基數爲 0 的狀況下,JavaScript 做以下處理:
  • 若是字符串 string 以"0x"或者"0X"開頭, 則基數是 16 (16 進制).
  • 若是字符串 string 以"0"開頭, 基數是 8(八進制)或者 10(十進制),那麼具體是哪一個基數由實現環境決- 定。ECMAScript 5 規定使用 10,可是並非全部的瀏覽器都遵循這個規定。所以,永遠都要明確給出radix參數的值。
  • 若是字符串 string 以其它任何值開頭,則基數是 10 (十進制)。
parseInt('1', 0);
  parseInt('2', 1);
  parseInt('3', 2);
複製代碼

因此答案是 [1, NaN, NaN]chrome

第 2 題

[typeof null, null instanceof Object];
複製代碼

typeof 返回一個表示類型的字符串。 instanceofinstanceof 運算符用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。 由於 typeof null === 'object' 自語言之初就是這樣....數組

typeof 的結果請看下錶:瀏覽器

type          // result
  undefined     // "undefined"
  null          // "object"
  Boolean       // "boolean"
  Number        // "number"
  String        // "string"
  Symbol        // "symbol"
  Host object   // Implementation-dependent
  Function      // "function"
  Object        // "object"
複製代碼

因此答案 [object, false]app

第 3 題

[[3, 2, 1].reduce(Math.pow), [].reduce(Math.pow)];
複製代碼
arr.reduce(callback[, initialValue])
複製代碼

reduce接受兩個參數, 一個回調, 一個初始值. 回調函數接受四個參數 previousValue, currentValue, currentIndex, array 須要注意的是 若是數組爲空且未提供初始值,則將引起類型錯誤。 因此第二個表達式會報異常. 第一個表達式等價於 Math.pow(3, 2) => 9; Math.pow(9, 1) =>9函數

答案 an errorpost

第 4 題

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

簡而言之 + 的優先級 大於 ?學習

因此原題等價於 'Value is true' ? 'Somthing' : 'Nonthing' 而不是 'Value is' + (true ? 'Something' : 'Nonthing')

答案 'Something'

第 5 題

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

javascript做用域中的變量提高。變量提高是 JavaScript 將聲明移至做用域 scope (全局域或者當前函數做用域) 頂部的行爲。

這題目至關於

var name = "World!";
(function() {
  var name; // 如今的name是undefined
  if (typeof name === "undefined") {
    name = "Jack";
    console.log("Goodbye " + name);
  } else {
    console.log("Hello " + name);
  }
})();
複製代碼

答案是 Goodbye Jack

第 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);
複製代碼

js 中能夠表示的最大整數不是 2 的 53 次方,而是 1.7976931348623157e+308。 2 的 53 次方不是 js 能表示的最大整數而應該是能正確計算且不失精度的最大整數,能夠參見 js 權威指南。 9007199254740992 +1 仍是 9007199254740992 ,這就是由於精度問題,若是 9007199254740992 +11 或者 9007199254740992 +111 的話,值是會發生改變的,只是這時候計算的結果不是正確的值,就是由於精度丟失的問題。

它進入無限循環

答案是 other

第 7 題

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

答案是 []

咱們來看一下 Array.prototype.filter 的 polyfill:

if (!Array.prototype.filter) {
  Array.prototype.filter = function(fun /*, thisArg*/) {
 "use strict";
    if (this === void 0 || this === null) {
      throw new TypeError();
    }
    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== "function") {
      throw new TypeError();
    }
    var res = [];
    var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
    for (var i = 0; i < len; i++) {
      if (i in t) {
        // 注意這裏!!!
        var val = t[i];
        if (fun.call(thisArg, val, i, t)) {
          res.push(val);
        }
      }
    }

    return res;
  };
}
複製代碼

咱們看到在迭代這個數組的時候, 首先檢查了這個索引值是否是數組的一個屬性, 那麼咱們測試一下.

0 in ary; => true
3 in ary; => false
10 in ary; => true
複製代碼

也就是說 從 3 - 9 都是沒有初始化的'坑'!, 這些索引並不存在與數組中. 在 array 的函數調用的時候是會跳過這些'坑'的.

第 8 題

var two = 0.2;
var one = 0.1;
var eight = 0.8;
var six = (0.6)[(two - one == one, eight - six == two)];
複製代碼

IEEE 754 標準中的浮點數並不能精確地表達小數

那何時精準, 何時不經準呢? 筆者也不知道...

答案 [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"));

// switch 是嚴格比較, String 實例和 字符串不同.

var s_prim = "foo";
var s_obj = new String(s_prim);

console.log(typeof s_prim); // "string"
console.log(typeof s_obj); // "object"
console.log(s_prim === s_obj); // false
複製代碼

字符串字面量和直接調用String()方法(不使用new調用構造函數)的結果是原始字符串。JS自動迴轉化原始字符串到String對象。因此能夠在原始字符串上使用用String對象的方法。而在上下文中,在原始字符串的方法被調用或者從其中獲取屬性時,JS會自動包裹原始字符串而後調用方法或者獲取屬性。

答案是 Do not know!

第 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"));
複製代碼

String(x) does not create an object but does return a string, i.e. typeof String(1) === "string"

仍是剛纔的知識點, 只不過 String 不只是個構造函數 直接調用返回一個字符串哦.

答案 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);
複製代碼
7 % 2 => 1
4 % 2 => 0
'13' % 2 => 1
-9 % % 2 => -1
Infinity % 2 => NaN
複製代碼

須要注意的是 餘數的正負號隨第一個操做數.

答案 [true, true, true, false, false]

第 12 題

parseInt(3, 8);
parseInt(3, 2);
parseInt(3, 0);
複製代碼

第一個值爲3沒什麼好說的。若是出現的數字不符合後面輸入的進制,則爲NaN,因此第二個值爲NaN。而radix爲0時的狀況第一題下面有介紹,這裏也是同樣爲默認10

答案 3, NaN, 3

第 13 題

Array.isArray(Array.prototype);
複製代碼

一個不爲人知的實事: Array.prototype => [];

答案: true

第 14 題

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

解析:

  • Boolean([0]) === true
    • [0] == true
    • true 轉換爲數字 => 1
    • [0] 轉化爲數字失敗, 轉化爲字符串 '0', 轉化成數字 => 0
    • 0 !== 1 a自己是一個長度爲1的數組,而當數組不爲空時,其轉換成bool值爲true。

而==左右的轉換,會使用若是一個操做值爲布爾值,則在比較以前先將其轉換爲數值的規則來轉換,Number([0]),也就是0,因而變成了0 == true,結果天然是false,因此最終結果爲B

答案:false

第 15 題

[] == [];
複製代碼

[] 是Object, 兩個 Object 不相等

當==運算符當左右都是對象時,則會比較其是否指向同一個對象。而每次調用字面量建立,都會創造新的對象,也就是會開闢新的內存區域。因此指針的值天然不同

答案是 false

第 16 題

"5" + 3;
"5" - 3;
複製代碼

+ 用來表示兩個數的和或者字符串拼接, -表示兩數之差.

- 會盡量的將兩個操做數變成數字, 而 + 若是兩邊不都是數字, 那麼就是字符串拼接.

答案是 '53', 2

第 17 題

var arr = Array(3);
arr[0] = 2;
arr.map(function(elem) {
  return "1";
});
複製代碼

稀疏數組. 同第 7 題.

題目中的數組實際上是一個長度爲3, 可是沒有內容的數組, array 上的操做會跳過這些未初始化的'坑'.

因此答案是 ["1", undefined × 2]

第 18 題

const a = {};
const b = { key: "b" };
const c = { key: "c" };

a[b] = 123;
a[c] = 456;

console.log(a[b]);
複製代碼

對象鍵自動轉換爲字符串。咱們試圖將一個對象設置爲對象 a 的鍵,其值爲123

可是,當對象自動轉換爲字符串化時,它變成了[Object object]。因此咱們在這裏說的a["Object object"] = 123是。而後,咱們能夠嘗試再次作一樣的事情。 c 對象一樣會發生隱式類型轉換。那麼,a["Object object"] = 456

而後,咱們打印a[b],它其實是a["Object object"]。咱們將其設置爲456,所以返回456

答案: 456

第 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);
複製代碼

這是一個大坑, 尤爲是涉及到 ES6 語法的時候

在調用函數時,函數內部的arguments維護着傳遞到這個函數的參數列表。它看起來是一個數組,但實際上它只是一個有length屬性的Object,不從Array.prototype繼承。因此沒法使用一些Array.prototype的方法。

arguments對象其內部屬性以及函數形參建立getter和setter方法,所以改變形參的值會影響到arguments對象的值,反過來也是同樣

也就是說 arguments 是一個 object, c 就是 arguments[2], 因此對於 c 的修改就是對 arguments[2] 的修改.

因此答案是 21.

然而!!!!!!

當函數參數涉及到 any rest parameters, any default parameters or any destructured parameters 的時候, 這個 arguments 就不在是一個 mapped arguments object 了.....

function sidEffecting(ary) {
  ary[0] = ary[2];
}
function bar(a, b, c = 3) {
  c = 10;
  sidEffecting(arguments);
  return a + b + c;
}
bar(1, 1, 1);
複製代碼

答案是: 12 !!!!

第 20 題

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

JavaScript 數字的題,與第七題考察點類似。因爲 JavaScript 實際上只有一種數字形式 IEEE 754 標準的 64 位雙精度浮點數,其所能表示的整數範圍爲-2^53~2^53(包括邊界值)。這裏的 111111111111111110000 已經超過了2^53 次方,因此會發生精度丟失的狀況。

答案仍是 111111111111111110000

第 21 題

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

這題考查的是函數調用時的 this 和 Array.prototype.reverse 方法。

The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.

也就是說 最後會返回這個調用者(this), 但是 x 執行的時候是上下文是全局. 那麼最後返回的是 window. 關於 this 指向問題

答案是 window

第 22 題

Number.MIN_VALUE > 0;
複製代碼

MIN_VALUE 屬性是 JavaScript 中可表示的最小的數(接近 0 ,但不是負數),它的近似值爲 5 x 10-324

有興趣的話能夠看一下這個MDN

答案是 true

列舉 Number 的其它常量:

  • Number.MAX_VALUE:最大的正數
  • Number.MIN_VALUE:最小的正數
  • Number.NaN:特殊值,用來表示這不是一個數
  • Number.NEGATIVE_INFINITY:負無窮大
  • Number.POSITIVE_INFINITY:正無窮大

第 23 題

[1 < 2 < 3, 3 < 2 < 1];
複製代碼

運算符的運算順序和隱式類型轉換的題,從MDN 上運算符優先級,'<'運算符順序是從左到右,因此變成了[true < 3, false < 1]

3 > 2 && 2 > 1;
// returns true

3 > 2 > 1;
// returns false because 3 > 2 is true, and true > 1 is false
// Adding parentheses makes things clear: (3 > 2) > 1
複製代碼

這個題等價於

1 < 2 => true;
 true < 3 =>  1 < 3 => true;
 3 < 2 => false;
 false < 1 => 0 < 1 => true;
複製代碼

因此,這裏首先經過**Number()**轉換爲數字而後進行比較,true 會轉換成 1,而 false 轉換成 0,就變成了[1 < 3, 0 < 1]

參考

答案是 [true, true]

第 24 題

// the most classic wtf
2 == [[[2]]];
複製代碼

又是隱式類型轉換的題,

也就是說左右兩邊都被轉換成了字符串,而字符串都是"2"

這裏首先須要對==右邊的數組進行類型轉換,根據如下規則(來自justjavac 的文章《「譯」JavaScript 的怪癖 1:隱式類型轉換》):

  1. 調用 valueOf()。若是結果是原始值(不是一個對象),則將其轉換爲一個數字。
  2. 不然,調用 toString() 方法。若是結果是原始值,則將其轉換爲一個數字。
  3. 不然,拋出一個類型錯誤。 第一步示例:
>3 * { valueOf: function () { return 5 } }
// 15
// 第三步示例:
>function returnObject() { return {} }
>3 * { valueOf: returnObject, toString: returnObject }

`TypeError: Cannot convert object to primitive value`
複製代碼

若是把對象轉換成字符串時,則轉換操做的第一步和第二步的順序會調換: 先嚐試 toString() 進行轉換,若是不是原始值,則再嘗試使用 valueOf()。

因此右側被使用 toString()方法轉換爲"2"

而後又經過 Number("2")轉換爲數字 2 進行比較,結果就是 true 了

答案是 true

第 25 題

3.toString()
3..toString()
3...toString()
複製代碼

JavaScript 會在調用方法時對原始值進行包裝,但在於這個點是小數點、仍是方法調用的點,因而乎第一個就是error了,由於 JavaScript 解釋器會將其第一個點認爲是小數點。

第二個則很好說通了,第一個點解釋爲小數點,變成了(3.0).toString(),結果就是"3"

第三個也是,第一個點爲小數點,第二個是方法調用的點,可是後面接的不是一個合法的方法名,因而乎就error

若是

var a = 3;
a.toString(); // "3"
複製代碼

這是由於在 js 中 1.1, 1., `.`` 都是合法的數字. 那麼在解析 3.toString 的時候這個** . **究竟是屬於這個數字仍是函數調用呢? 只能是數字, 由於 3.合法啊!

答案是 error, '3', error

第 26 題

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

變量提高和隱式定義全局變量的題,也是一個 JavaScript 經典的坑...

y 被賦值到全局. x 是局部變量. 因此打印 x 的時候會報 ReferenceError: x is not defined

答案是 1, error

第 27 題

var a = /123/,
  b = /123/;
a == b;
a === b;
複製代碼

JavaScript 中的正則表達式依舊是對象,==運算符左右兩邊都是對象時,會比較他們是否指向同一個對象.

答案 false, false

第 28 題

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

JavaScript 中 Array 的本質也是,因此前兩個的結果都是 false,

而 JavaScript 中 Array 的'>'運算符和'<'運算符的比較方式相似於字符串比較字典序

會從第一個元素開始進行比較,若是同樣比較第二個,還同樣就比較第三個,如此類推,因此第三個結果爲 false,第四個爲 true。

答案 false, false, false, true

第 29 題

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

原型鏈的題(總會有的),考查的__proto__prototype的區別。首先要明確對象和構造函數的關係,對象在建立的時候,其__proto__會指向其構造函數的prototype屬性

實例對象是沒有prototype屬性的,只有函數纔有,因此a.prototype實際上是undefined,第一個結果爲 false

Object其實是一個構造函數(typeof Object的結果爲"function"),使用字面量建立對象和new Object建立對象是同樣的,因此a.__proto__也就是Object.prototype,而Object.getPrototypeOf(a)與a.__proto__是同樣的,因此第二個結果爲true

答案 [false, true]

第 30 題

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

f.prototype 是使用使用 new 建立的 f 實例的原型

而 Object.getPrototypeOf 是 f 函數的原型.

a === Object.getPrototypeOf(new f()) // true
b === Function.prototype // true
複製代碼

答案 false

第 31 題

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

考察了函數的 name 屬性,使用函數定義方式時,會給 function 對象自己添加一個 name 屬性,保存了函數的名稱,很好理解oldName爲"foo"。name 屬性時只讀的,不容許修改,因此foo.name = "bar";以後,foo.name仍是"foo"

答案 ['foo', 'foo']

第 32 題

"1 2 3".replace(/\d/g, parseInt);
複製代碼

String.prototype.replace、正則表達式的全局匹配和 parseInt,

首先須要肯定 replace 會傳給 parseInt 哪些參數。舉個栗子:

"1 2 3".replace(/\d/g, function() {
  console.log(arguments);
});
//輸出結果:
//["1", 0, "1 2 3"]
//["2", 2, "1 2 3"]
//["3", 4, "1 2 3"]
複製代碼

一共三個:

  1. match:正則表達式被匹配到的子字符串
  2. offset:被匹配到的子字符串在原字符串中的位置
  3. string:原字符串

這樣就很好理解了,又回到以前 parseInt 的問題了,結果就是 parseInt("1", 10), parseInt("2", 2), parseInt("3", 4)

答案: 1, NaN, 3

第 33 題

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

第一 f.name 值爲"f",第三 eval("f")則會輸出 f 函數,因此結果爲"function"

第二在本身的瀏覽器測試的時候是 '', 第四是 'undefined'

第 34 題

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

正則表達式的**test()**會自動將參數轉換爲字符串,原式就變成了[lowerCaseOnly.test("null"), lowerCaseOnly.test("undefined")]

答案:[true, true]

第 35 題

[, , ,].join(", ");
複製代碼
[,,,] => [empty × 3]
複製代碼

JavaScript 中使用字面量建立數組時,若是最末尾有一個逗號',',會背省略,因此這是個長度爲三的稀疏數組(這是長度爲三, 並無 0, 1, 2 三個屬性哦)(都是 undefined);

答案: ", , "

第 36 題

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

經典坑中的一個,class 是關鍵字。根據瀏覽器的不一樣,結果不一樣:

chrome 的結果: "Animal"

Firefox 的結果:"Animal"

Opera 的結果:"Animal"

IE 8 以上也是: "Animal"

IE 8 及如下: 報錯

因此答案不重要, 重要的是本身在取屬性名稱的時候儘可能避免保留字. 若是使用的話請加引號 a['class']

第 37 題

var a = new Date("epoch");
複製代碼

簡單來講, 若是調用 Date 的構造函數傳入一個字符串的話須要符合規範, 即知足 Date.parse 的條件.

另外須要注意的是 若是格式錯誤 構造函數返回的還是一個 Date 的實例 Invalid Date.

答案 Invalid Date

第 38 題

var a = Function.length,
  b = new Function().length;
a === b;
複製代碼

咱們知道一個function(Function 的實例)的 length 屬性就是函數簽名的參數個數, 因此 b.length == 0.

另外 Function.length 定義爲 1......

因此不相等......

答案 false

第 39 題

var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c];
複製代碼
  • 若是不傳參數等價於當前時間.
  • 若是是函數調用 返回一個字符串.

答案 [false, false, false]

第 40 題

var min = Math.min(),
  max = Math.max();
min < max;
複製代碼

Math.min 的參數是 0 個或者多個。若是是多個參數很容易理解,返回參數中最小的。

若是沒有參數,則返回InfinityInfinity 是 javascript 中全局對象的一個屬性,在瀏覽器環境中就是 window 對象的一個屬性,表示無窮大

Math.max() 沒有傳遞參數時返回的是 -Infinity

所以 Math.min() 要比 Math.max() 大。

答案 false

第 41 題

function captureOne(re, str) {
  var match = re.exec(str);
  return match && match[1];
}
var numRe = /num=(\d+)/gi,
  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];
複製代碼

由於第一個正則有一個 g 選項 它會‘記憶’他所匹配的內容, 等匹配後他會從上次匹配的索引繼續, 而第二個正則不會

因此 a1 = '1'; a2 = '1'; a3 = null; a4 = '2'

答案: [true, false]

第 42 題

var a = new Date("2014-03-19"),
  b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()];
a.getDay(); // 3
b.getDay(); // 6
a.getMonth(); // 2
b.getMonth(); // 3
複製代碼

new Date("2014-03-19") 若是傳入的是一個符合規則的日期字符串("2014-03-19"),獲得的就是對應的標準時間對象 // Wed Mar 19 2014 08:00:00 GMT+0800 (中國標準時間)

new Date(2014, 03, 19) 若是傳入的是三個,第三個識別爲日期// Wed Mar 19 2014 08:00:00 GMT+0800 (中國標準時間)

答案:[false, false]

第 43 題

if ("http://giftwrapped.com/picture.jpg".match(".gif")) {
  ("a gif file");
} else {
  ("not a gif file");
}
複製代碼

tring.prototype.match 接受一個正則, 若是不是, 按照 new RegExp(obj) 轉化. 因此 . 並不會轉義 那麼 /gif 就匹配了 /.gif/

答案: 'a gif file'

第 44 題

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

在兩個函數裏, a做爲參數其實已經聲明瞭, 因此 var a; var a = 'bye' 其實就是 a; a ='bye'

答案: ["hello", "bye"]

後言

我只是一個分享者,代碼的搬運工、一個追求者、一個學習者,有不一樣的意見或新的想法,提出來,咱們一塊兒研究。

魯迅曾經說過**知識遍地,拾到了就是你的**。

相似文章: 悅老闆

相關文章
相關標籤/搜索