僅以此文記念這些年失敗的面試和逝去的頭髮。。html
console.log(2 + '2')
答案:
'22'
解析:
這是比較常規的面試題了,主要考察的是 JavaScript 中的隱式類型轉換。在 JS 中 +
主要有兩個做用:數字相加和字符串拼接,當 +
兩邊不都爲數字時會把它們都轉爲字符串再拼接,因此第一個 2
會先被轉成 '2'
再與第二個 '2'
拼接。node
console.log(2 - '2')
答案:
0
解析:
和 +
不一樣,-
沒有操做字符串而只有 「減法」 的功能,當 -
兩邊有非數字時會先把其轉換成數字再相減。因此,本題中的 '2'
先被轉成數字 2
,最終 2 - 2
等於 0
。當操做數無法轉換成數字時則會致使結果爲 NaN
,好比 'foo' - 2 = NaN
。git
*
、/
、%
的行爲也和-
相似。
console.log(true + 1)
答案:
2
解析:
有了第1題的經驗,咱們很容易就認爲 true
和 1
都會被轉成字符串,但實際上 JS 中 true == 1
, false == 0
,因此 true
會被轉成 1
再執行加法。github
注意:true
只等於1
,false
只等於0
,等於其餘數字是不成立的,如true == 2
爲false
。
答案:
false
解析:
NaN 表示一個不爲數字的值(Not a number)。咱們只須要記住:NaN
和全部值都不等,包括它本身,不論是用 ==
仍是 ===
判斷!判斷一個值是否爲 NaN
只能用 isNaN()
或者 Number.isNaN()
。面試
console.log(5 < 6 < 7)
2. console.log(7 > 6 > 5)
答案:(1)true
;(2)false
解析:
根據第三題的經驗 true == 1
,false == 0
,5 < 6 === true
而 1 < 7
,因此 1 爲 true
;7 > 6 === true
而 1 > 5 === false
,因此 2 爲 false
。算法
0.1 + 0.2 = ?
答案:
0.30000000000000004
解析:
問題的關鍵不在於答案裏面有幾個 0,而在於它不等於 0.3!Javascript 中的數字使用的是 64 位雙精度浮點型(可參考 ECMAScript 規範)。如同十進制不能精確表示 1/3
對應的小數,二進制也沒有辦法精確表示 0.1
、0.2
這種小數,好比 0.1
轉成二進制爲:0.0001100110011001100110011001100110011001100110011001101
,是無限循環的小數,而由於計算機不可能無限分配內存去存儲這個數,通常只能精確到多少位,因此形成精度丟失在所不免,最終致使計算結果有所誤差。express
[1, 2, 3] + [4, 5, 6] = ?
答案:
1,2,34,5,6
解析:
本題主要考察隱式類型轉換和數組轉字符串,咱們已經知道 +
兩邊若是不都爲數字則會把它們轉成字符串再拼接,而 [1, 2, 3].toString() === '1,2,3'
,由於最終結果爲 '1,2,3' + '4,5,6' === '1,2,34,5,6'
。數組
若是咱們想要進行數組拼接能夠:安全
[1, 2, 3].concat([4, 5, 6]); // 或者使用spread opertator [...[1, 2, 3], ...[4, 5, 6]];
(function () { var a = b = 100; })(); console.log(a); console.log(b);
答案:報錯(
Uncaught ReferenceError: a is not defined
)
解析:
因爲賦值表達式是從右往左執行的,至關於 var a = (b = 100);
,因此先執行 b = 100
,因爲函數體中並無局部變量 b
,因此會定義一個全局變量 b
並賦值 100
;接着執行 a = b
,會把b的值賦值給局部變量 a
。執行 console.log(a)
時會直接報錯,由於全局做用域中並無定義 a
,因爲報錯致使程序中斷因此 console.log(b)
沒有執行。若是把 console.log(b)
放在前面就是先打印 100
再報錯了。函數
使用嚴格模式('use strict'
)能夠避免b
這種意外全局變量的建立。
for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); }, 0); }
答案:
5 5 5 5 5
解析:
這是個經典問題,考察對做用域和 event loop 的理解。用 var
定義的變量的做用域是函數做用域(functional scope),因此上面代碼至關於:
var i; for (i = 0; i < 5; i++) { ... }
最終 console.log(i)
裏面的 i
引用的是外面的那個,而因爲event loop機制,setTimeout
的回調函數會被push到task queue裏面等call stack裏面的for循環結束了再執行,此時 i
已經變成了5,所以最終打印結果變成5個5。隨着ES6的普及,var
使用的機會愈來愈少,let
、const
成爲主流,本題若是想打印 0 1 2 3 4
,只要把 var
換成 let
就好了。
function foo() { return x; function x() {} } console.log(typeof foo());
答案:
'function'
解析:
本題考察的是對函數提高的理解。通常定義一個函數有兩種方式:
function fn(){ ... }
var fn = function(){ ... }
其中函數聲明會存在函數提高的現象,而函數表達式則沒有(函數表達式會存在變量提高,但初始化並不會被提高,因此不具備函數提高的效果),即JS在編譯階段會把對函數的定義提高到做用域頂部(實際上並不會修改代碼結構,而是在內存中進行處理),因此本題的代碼等價於:
function foo() { function x() { console.log(`hi`); } return x; }
因此,結果打印 function
。函數提高的主要做用是能夠在函數定義以前就進行調用。
var x = 1; function x(){} console.log(typeof x);
答案:
'number'
解析:
本題仍是考察變量提高和函數提高,以及它們的優先級。函數提高的優先級要高於變量提高,因此函數被提高到做用域最頂部,接下來纔是變量定義,所以本題等價於:
function x(){} var x; x = 1; console.log(typeof x);
因此,x
最終是數字。
const fn = () => arguments; console.log(fn("hi"));
答案:報錯
Uncaught ReferenceError: arguments is not defined
解析:
本題主要考察箭頭函數的特色。箭頭函數沒有本身的 this
和 arguments
,而是引用的外層做用域中的,而全局沒有定義 arguments
變量,因此報錯。
在箭頭函數中若是要訪問參數集,建議使用 Rest parameters:
(...args) => { }
const fn = function () { return { message: "hello"; } }; console.log(fn());
答案:
undefined
解析:
在 JavaScript 中,若是 return
關鍵詞和返回值之間存在換行符(Line Terminator),則 return
後面會自動插入 ';'
,參考 ASI (Automatic semicolon insertion)。因此本題代碼等同於:
const fn = function () { return; { message: "hello"; } }; console.log(fn());
結果所以爲 undefined
。
setTimeout(() => { console.log("a"); }, 1); setTimeout(() => { console.log("b"); }, 0);
答案:有多是'a' 'b'
,也有多是'b' 'a'
,取決於 js 運行環境。
0ms
和 1ms
是等價的,由於 0
會被轉成 1
(可參考Node源碼),因此在 node 中運行結果是 'a' 'b'
'a' 'b'
'b' 'a'
該題屬於「回」字有多少種寫法那一類的,並沒有多大的實際價值 😢。
event.target
和 event.currentTarget
的區別答案:event.target
是真正觸發 event 的元素,而event.currentTarget
是綁定 event handler 的元素。
例如:
<div id="container"> <button>click me</button> </div>
const container = document.getElementById("container"); container.addEventListener("click", function (e) { console.log("target =", e.target); console.log("currentTarget =", e.currentTarget); });
function(){ console.log('hi'); }()
答案:報錯:
Uncaught SyntaxError: Function statements require a function name
解析:
本題主要考察對 IIFE 語法的理解。本題代碼等價於:
function(){ console.log('hi'); } ()
因此報語法錯誤,而正確的 IIFE 語法應該是 (function(){...})()
。
const arr = [1, 2, 3]; arr[-1] = -1; console.log(arr[arr.indexOf(100)]);
答案:
-1
解析:
本題主要考察對 JavaScript 對象的理解和數組的 indexOf()
方法。首先,數組本質仍是一個 JavaScript 對象,那就能夠設置 屬性,就算數組的索引沒有 -1,但 -1 仍可做爲對象的 key 存在,因此 x[-1] = -1
沒有問題。接着,indexOf()
方法所要查找的值若是在數組中不存在則返回 -1,因此最終至關於求 console.log(arr[-1])
,獲得最終答案爲 -1
。
const arr = [5, 22, 1, 14, 2, 56, 132, 88, 12]; console.log(arr.sort());
答案:
[1, 12, 132, 14, 2, 22, 5, 56, 88]
解析:
本題主要考察對數組 sort()
方法的理解。 sort()
默認是把元素轉成字符串,再比較 UTF-16 編碼的單元值序列進行升序排列。好比 2
和 12
的 UTF-16 編碼分別爲 50
和 49, 50
,而 49 < 50
,因此 12
排在 2
以前。
若是想按照實際的數字大小升排列須要傳入一個比較函數:
// 升序 arr.sort((a, b) => a - b); // 降序 arr.sort((a, b) => b - a);
x
的值使下列等式同時爲 true
x * x === 0; x + 1 === 1; x - 1 === -1; x / x === 1;
答案:
Number.MIN_VALUE
解析:
Number.MIN_VALUE 是 JavaScript 能表示的最小的正數,也是最接近 0 的值,因此不少行爲和 0 相似,例如前 3 條等式,可是它畢竟不是 0,因此能夠做爲除數,所以等式 4 也成立。與之相對的還有 Number.MAX_VALUE,是 JavaScript 中能表示的最大數。
答案:
10000000000000000
解析:
看到答案有點懵,直覺告訴咱們這確定又和 JavaScript 中的某些最大數的限制有關。沒錯,JS 中有個 Number.MAX_SAFE_INTEGER,它的值爲 2^53 - 1
,即 9007199254740991
。這個數的存在仍是由於 JS 使用的 64 位雙精度浮點型數,它能表示的區間僅僅爲 -(2^53 - 1) ~ 2^53 - 1
,超過這個區間的數就不「安全」了,不安全表現爲沒法準確的表示和比較這些數,好比 Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2
結果爲 true
。Number.isSafeInteger()
能夠用來判斷一個數是否 「安全」。
當咱們須要使用更大的數時建議使用 BigInt。
答案:null
解析:
通常認爲原型鏈頂層是 Object.prototype
,但其實 Object.prototype
仍是有 __proto__
的內部屬性的,而 Object.prototype.__proto__
等於 null
。因此答案爲 null
更爲準確。
參考 Annotated ECMAScript 5.1 - Properties of the Object Prototype Object
好比:
const obj = {}; // todo: 讓 obj.p = 1 無效 obj.p = 1;
答案:
至少有四種方法:
Object.freeze(obj)
Object.seal(obj)
Object.preventExtensions(obj)
Object.defineProperty(obj, 'p', { writable: false })
解析:
Object.freeze()
最爲嚴格,它會徹底禁止對象作任何修改,包括:增長新屬性、修改已有屬性、修改其原型Object.seal()
的規則寬鬆一點:容許修改 writable
的屬性,但不容許新增和刪除屬性,且已有屬性都會被標記爲不可配置的(non-configurable)Object.preventExtensions()
更加寬鬆,能夠阻止對象新增屬性和修改其 __proto__
(不能給 __proto__
從新賦值)Object.defineProperty()
將屬性 p
定義爲不可寫的,所以沒法再給 p
設置新的值(writable
默認爲 false
,能夠省略)基礎算法題,至少有2種方法:
解法1:將數字轉成字符串,再轉成數組,翻轉後再比較:
function palindrome(str) { str = str.toLowerCase(); return str.split("").reverse().join("") === str; }
解法2:for循環,頭尾比較
function palindrome(str) { for (var i = 0; i < str.length / 2; i++) { const left = str[i]; const right = str[str.length - 1 - i]; if (left.toLowerCase() !== right.toLowerCase()) return false; } return true; }
這道題的升級版,是判斷一個數字是否爲迴文,且不能將數字轉成字符串。思路是經過取餘的方法獲取到每一位的數字,再構造一個反過來的數和原數進行比較:
function palindrome(num) { let copy = num; let currentDigit = 0; let reversedNum = 0; do { currentDigit = copy % 10; reversedNum = reversedNum * 10 + currentDigit; copy = parseInt(copy / 10); } while (copy !== 0); return num === reversedNum; }
好了,先想到這些。若是本文對你有幫助,給個贊吧!