譯者按: 總結了大量JavaScript基本知識點,很有用!
為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用於學習。
根據StackOverflow調查, 自2014年一來,JavaScript是最流行的編程語言。當然,這也在情理之中,畢竟1/3的開發工作都需要一些JavaScript知識。因此,如果你希望在成為一個開發者,你應該學會這門語言。
這篇博客的主要目的是將所有面試中常見的概念總結,方便你快速去了解。(鑒於本文內容過長,方便閱讀,將分為三篇博客來翻譯, 此為第二部分。第一部分請點擊快速掌握JavaScript面試基礎知識(一))
閉包
閉包由一個函數以及該函數定義是所在的環境組成。我們通過例子來形象解釋它。
function sayHi(name){ |
請理解var sayHiToJon = sayHi('Jon');這行代碼的執行過程,sayHi函數執行,首先將message的值計算出來;然后定義了greeting函數,函數中引用了message變量;最后,返回greeting函數。
如果按照C/Java語言的思路,sayHiToJon就等價於greeting函數,那么會報錯:message未定義。但是在JavaScript中不一樣,這里的sayHiToJon函數等於greeting函數以及一個環境,該環境中包含了message。因此,當我們調用sayHiToJon函數,可以成功地將message打印出來。因此,這里的閉包就是greeting函數和一個包含message變量的環境。(備注: 為了便於理解,此段落未按照原文翻譯。)
閉包的一個優勢在於數據隔離。我們同樣用一個例子來說明:
function SpringfieldSchool() { |
在elementary被創建的時候,SpringfieldSchool已經返回。也就是說staff無法被外部訪問。唯一可以訪問的方式就是里面的閉包函數getStaff和addStaff。
我們來看一個面試題:下面的代碼有什么問題,如何修復?
const arr = [10, 12, 15, 21]; |
上面的代碼輸出的結果全部都一樣:”The value undefined is at index: 4”。因為所有在setTimeout中定義的匿名函數都引用了同一個外部變量i。當匿名函數執行的時候,i的值為4。
這個問題可以改用IIFE(后面會介紹)方法來解決,通過對每一個匿名函數構建獨立的外部作用域來實現。
const arr = [10, 12, 15, 21]; |
當然,還有一個方法,使用let來聲明i。
const arr = [10, 12, 15, 21]; |
立即調用的函數表達式(Immediate Invoked Function Expression)(IIFE)
一個IIFE是一個函數表達式在定義之后立即被調用。常用在你想對一個新聲明的變量創建一個隔離的作用域。
它的格式為: (function(){....})()。前面的大括號用於告訴編譯器這里不僅僅是函數定義,后面的大括號用於執行該函數。
var result = []; |
使用IIFE可以:
- 為函數綁定私有數據
- 創建一個新的環境
- 避免污染全局命名空間
環境(Context)
我們往往容易將環境(Context)和作用域(Scope)搞混,我來簡單解釋一下:
- 環境(Context): 由函數如何被調用而決定,往往指
this。 - 作用域(Scope): 可訪問的變量。
函數調用:call, apply, bind
這三個方法都是為了將this綁定到函數,區別在於調用的方式。
.call()會立即執行函數,你需要把參數按順序傳入;.apply()會立即執行函數,你需要把所有的參數組合為一個數組傳入;
.call()和.apply()幾乎相同。哪個傳入參數方便,你就選擇哪個。
const Snow = {surename: 'Snow'} |
注意:如果你將數組傳入call函數,它會認為只有一個參數。
ES6允許使用新的操作符將數組變換為一個序列。
char.knows.call(Snow, ...["nothing", "Jon"]); // You know nothing, Jon Snow |
.bind()返回一個新的函數,以及相應的環境和參數。如果你想該函數稍后調用,那么推薦使用bind。.bind()函數的優點在於它可以記錄一個執行環境,對於異步調用和事件驅動的編程很有用。
.bind()傳參數的方式和call相同。
const Snow = {surename: 'Snow'} |
this關鍵字
要理解JavaScript中this關鍵字,特別是它指向誰,有時候相當地復雜。this的值通常由函數的執行環境決定。簡單的說,執行環境指函數如何被調用的。this像是一個占位符(placeholder),它指向當方法被調用時,調用對應的方法的對象。
下面有序地列出了判斷this指向的規則。如果第一條匹配,那么就不用去檢查第二條了。
-
new綁定 - 當使用new關鍵字調用函數的時候,this指向新構建的對象。function Person(name, age) {
this.name = name;
this.age =age;
console.log(this);
}
const Rachel = new Person('Rachel', 30); // { age: 30, name: 'Rachel' } -
顯示綁定(Explicit binding) - 當使用
call或則apply的時候,我們顯示的傳入一個對象參數,該參數會綁定到this。 注意:.bind()函數不一樣。用bind定義一個新的函數,但是依然綁定到原來的對象。function fn() {
console.log(this);
}
var agent = {id: '007'};
fn.call(agent); // { id: '007' }
fn.apply(agent); // { id: '007' }
var boundFn = fn.bind(agent);
boundFn(); // { id: '007' } -
隱式綁定 - 當一個函數在某個環境下調用(在某個對象里),
this指向該對象。也就是說該函數是對象的一個方法。var building = {
floors: 5,
printThis: function() {
console.log(this);
}
}
building.printThis(); // { floors: 5, printThis: function() {…} } -
默認綁定 - 如果上面所有的規則都不滿足,那么
this指向全局對象(在瀏覽器中,就是window對象)。當函數沒有綁定到某個對象,而單獨定義的時候,該函數默認綁定到全局對象。function printWindow() {
console.log(this)
}
printWindow(); // window object
注意:下面的情況中,inner函數中的this指向全局。
function Dinosaur(name) { |
- 詞法(Lexical) this - 當是使用
=>來定義函數時,this指向定義該函數時候外層的this。 備注:大概是和定義的詞法(=>)有關,把它稱作Lexical this。
function Cat(name) { |
嚴格(Strict)模式
如果你使用了"use strict"指令,那么JavaScript代碼會在嚴格模式下執行。在嚴格模式下,對於詞法分析和錯誤處理都有特定的規則。在這里我列出它的一些優點:
- 使得Debug更容易:以前會被忽略的錯誤現在會顯示報錯,比如賦值給一個不可寫的全局變量或則屬性;
- 避免不小心聲明了全局變量:賦值給一個未定義的變量會報錯;
- 避免無效使用delete:嘗試去刪除變量、函數或則不可刪除的屬性會拋出錯誤;
- 避免重復的屬性名和參數值:對象上重復的屬性和函數參數會拋出錯誤(在ES6中不再是這樣);
- 使得
eval()更加安全:在eval()中定義的變量和函數在外部作用域不可見; - “安全”的消除JavaScript中this的轉換:如果
this是null或則undefined不在轉換到全局對象。也就是說在瀏覽器中使用this去指向全局對象不再可行。
對於在嚴格(strict)模式和測試階段都沒有發現的bug,不妨接入線上實時監控插件Fundebug。
版權聲明: 轉載時請注明作者Fundebug以及本文地址: https://blog.fundebug.com/2018/01/22/the-definitive-javascript-handbook-for-a-developer-interview-2/


