摘要:第一部分請點擊快速掌握面試基礎(chǔ)知識一閉包閉包由一個函數(shù)以及該函數(shù)定義是所在的環(huán)境組成。當匿名函數(shù)執(zhí)行的時候,的值為。這個問題可以改用后面會介紹方法來解決,通過對每一個匿名函數(shù)構(gòu)建獨立的外部作用域來實現(xiàn)。
譯者按: 總結(jié)了大量JavaScript基本知識點,很有用!
原文: The Definitive JavaScript Handbook for your next developer interview
為了保證可讀性,本文采用意譯而非直譯。另外,本文版權(quán)歸原作者所有,翻譯僅用于學(xué)習(xí)。
根據(jù)StackOverflow調(diào)查, 自2014年一來,JavaScript是最流行的編程語言。當然,這也在情理之中,畢竟1/3的開發(fā)工作都需要一些JavaScript知識。因此,如果你希望在成為一個開發(fā)者,你應(yīng)該學(xué)會這門語言。
這篇博客的主要目的是將所有面試中常見的概念總結(jié),方便你快速去了解。(鑒于本文內(nèi)容過長,方便閱讀,將分為三篇博客來翻譯, 此為第二部分。第一部分請點擊快速掌握JavaScript面試基礎(chǔ)知識(一))
閉包閉包由一個函數(shù)以及該函數(shù)定義是所在的環(huán)境組成。我們通過例子來形象解釋它。
function sayHi(name){ var message = `Hi ${name}!`; function greeting() { console.log(message) } return greeting } var sayHiToJon = sayHi("Jon"); console.log(sayHiToJon) // ?() { console.log(message) } console.log(sayHiToJon()) // "Hi Jon!"
請理解var sayHiToJon = sayHi("Jon");這行代碼的執(zhí)行過程,sayHi函數(shù)執(zhí)行,首先將message的值計算出來;然后定義了greeting函數(shù),函數(shù)中引用了message變量;最后,返回greeting函數(shù)。
如果按照C/Java語言的思路,sayHiToJon就等價于greeting函數(shù),那么會報錯:message未定義。但是在JavaScript中不一樣,這里的sayHiToJon函數(shù)等于greeting函數(shù)以及一個環(huán)境,該環(huán)境中包含了message。因此,當我們調(diào)用sayHiToJon函數(shù),可以成功地將message打印出來。因此,這里的閉包就是greeting函數(shù)和一個包含message變量的環(huán)境。(備注: 為了便于理解,此段落未按照原文翻譯。)
閉包的一個優(yōu)勢在于數(shù)據(jù)隔離。我們同樣用一個例子來說明:
function SpringfieldSchool() { let staff = ["Seymour Skinner", "Edna Krabappel"]; return { getStaff: function() { console.log(staff) }, addStaff: function(name) { staff.push(name) } } } let elementary = SpringfieldSchool() console.log(elementary) // { getStaff: ?, addStaff: ? } console.log(staff) // ReferenceError: staff is not defined /* Closure allows access to the staff variable */ elementary.getStaff() // ["Seymour Skinner", "Edna Krabappel"] elementary.addStaff("Otto Mann") elementary.getStaff() // ["Seymour Skinner", "Edna Krabappel", "Otto Mann"]
在elementary被創(chuàng)建的時候,SpringfieldSchool已經(jīng)返回。也就是說staff無法被外部訪問。唯一可以訪問的方式就是里面的閉包函數(shù)getStaff和addStaff。
我們來看一個面試題:下面的代碼有什么問題,如何修復(fù)?
const arr = [10, 12, 15, 21]; for (var i = 0; i < arr.length; i++) { setTimeout(function() { console.log(`The value ${arr[i]} is at index: ${i}`); }, (i+1) * 1000); }
上面的代碼輸出的結(jié)果全部都一樣:"The value undefined is at index: 4"。因為所有在setTimeout中定義的匿名函數(shù)都引用了同一個外部變量i。當匿名函數(shù)執(zhí)行的時候,i的值為4。
這個問題可以改用IIFE(后面會介紹)方法來解決,通過對每一個匿名函數(shù)構(gòu)建獨立的外部作用域來實現(xiàn)。
const arr = [10, 12, 15, 21]; for (var i = 0; i < arr.length; i++) { (function(j) { setTimeout(function() { console.log(`The value ${arr[j]} is at index: ${j}`); }, j * 1000); })(i) }
當然,還有一個方法,使用let來聲明i。
const arr = [10, 12, 15, 21]; for (let i = 0; i < arr.length; i++) { setTimeout(function() { console.log(`The value ${arr[i]} is at index: ${i}`); }, (i) * 1000); }立即調(diào)用的函數(shù)表達式(Immediate Invoked Function Expression)(IIFE)
一個IIFE是一個函數(shù)表達式在定義之后立即被調(diào)用。常用在你想對一個新聲明的變量創(chuàng)建一個隔離的作用域。
它的格式為: (function(){....})()。前面的大括號用于告訴編譯器這里不僅僅是函數(shù)定義,后面的大括號用于執(zhí)行該函數(shù)。
var result = []; for (var i=0; i < 5; i++) { result.push( function() { return i } ); } console.log( result[1]() ); // 5 console.log( result[3]() ); // 5 result = []; for (var i=0; i < 5; i++) { (function () { var j = i; // copy current value of i result.push( function() { return j } ); })(); } console.log( result[1]() ); // 1 console.log( result[3]() ); // 3
使用IIFE可以:
為函數(shù)綁定私有數(shù)據(jù)
創(chuàng)建一個新的環(huán)境
避免污染全局命名空間
環(huán)境(Context)我們往往容易將環(huán)境(Context)和作用域(Scope)搞混,我來簡單解釋一下:
環(huán)境(Context): 由函數(shù)如何被調(diào)用而決定,往往指this。
作用域(Scope): 可訪問的變量。
函數(shù)調(diào)用:call, apply, bind這三個方法都是為了將this綁定到函數(shù),區(qū)別在于調(diào)用的方式。
.call()會立即執(zhí)行函數(shù),你需要把參數(shù)按順序傳入;
.apply()會立即執(zhí)行函數(shù),你需要把所有的參數(shù)組合為一個數(shù)組傳入;
.call()和.apply()幾乎相同。哪個傳入?yún)?shù)方便,你就選擇哪個。
const Snow = {surename: "Snow"} const char = { surename: "Stark", knows: function(arg, name) { console.log(`You know ${arg}, ${name} ${this.surename}`); } } char.knows("something", "Bran"); // You know something, Bran Stark char.knows.call(Snow, "nothing", "Jon"); // You know nothing, Jon Snow char.knows.apply(Snow, ["nothing", "Jon"]); // You know nothing, Jon Snow
注意:如果你將數(shù)組傳入call函數(shù),它會認為只有一個參數(shù)。
ES6允許使用新的操作符將數(shù)組變換為一個序列。
char.knows.call(Snow, ...["nothing", "Jon"]); // You know nothing, Jon Snow
.bind()返回一個新的函數(shù),以及相應(yīng)的環(huán)境和參數(shù)。如果你想該函數(shù)稍后調(diào)用,那么推薦使用bind。
.bind()函數(shù)的優(yōu)點在于它可以記錄一個執(zhí)行環(huán)境,對于異步調(diào)用和事件驅(qū)動的編程很有用。
.bind()傳參數(shù)的方式和call相同。
const Snow = {surename: "Snow"} const char = { surename: "Stark", knows: function(arg, name) { console.log(`You know ${arg}, ${name} ${this.surename}`);} } const whoKnowsNothing = char.knows.bind(Snow, "nothing"); whoKnowsNothing("Jon"); // You know nothing, Jon Snowthis關(guān)鍵字
要理解JavaScript中this關(guān)鍵字,特別是它指向誰,有時候相當?shù)貜?fù)雜。this的值通常由函數(shù)的執(zhí)行環(huán)境決定。簡單的說,執(zhí)行環(huán)境指函數(shù)如何被調(diào)用的。this像是一個占位符(placeholder),它指向當方法被調(diào)用時,調(diào)用對應(yīng)的方法的對象。
下面有序地列出了判斷this指向的規(guī)則。如果第一條匹配,那么就不用去檢查第二條了。
new綁定 - 當使用new關(guān)鍵字調(diào)用函數(shù)的時候,this指向新構(gòu)建的對象。
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的時候,我們顯示的傳入一個對象參數(shù),該參數(shù)會綁定到this。 注意:.bind()函數(shù)不一樣。用bind定義一個新的函數(shù),但是依然綁定到原來的對象。
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" }
隱式綁定 - 當一個函數(shù)在某個環(huán)境下調(diào)用(在某個對象里),this指向該對象。也就是說該函數(shù)是對象的一個方法。
var building = { floors: 5, printThis: function() { console.log(this); } } building.printThis(); // { floors: 5, printThis: function() {…} }
默認綁定 - 如果上面所有的規(guī)則都不滿足,那么this指向全局對象(在瀏覽器中,就是window對象)。當函數(shù)沒有綁定到某個對象,而多帶帶定義的時候,該函數(shù)默認綁定到全局對象。
function printWindow() { console.log(this) } printWindow(); // window object
注意:下面的情況中,inner函數(shù)中的this指向全局。
function Dinosaur(name) { this.name = name; var self = this; inner(); function inner() { alert(this); // window object — the function has overwritten the "this" context console.log(self); // {name: "Dino"} — referencing the stored value from the outer context } } var myDinosaur = new Dinosaur("Dino");
詞法(Lexical) this - 當是使用=>來定義函數(shù)時,this指向定義該函數(shù)時候外層的this。 備注:大概是和定義的詞法(=>)有關(guān),把它稱作Lexical this。
function Cat(name) { this.name = name; console.log(this); // { name: "Garfield" } ( () => console.log(this) )(); // { name: "Garfield" } } var myCat = new Cat("Garfield");嚴格(Strict)模式
如果你使用了"use strict"指令,那么JavaScript代碼會在嚴格模式下執(zhí)行。在嚴格模式下,對于詞法分析和錯誤處理都有特定的規(guī)則。在這里我列出它的一些優(yōu)點:
使得Debug更容易:以前會被忽略的錯誤現(xiàn)在會顯示報錯,比如賦值給一個不可寫的全局變量或則屬性;
避免不小心聲明了全局變量:賦值給一個未定義的變量會報錯;
避免無效使用delete:嘗試去刪除變量、函數(shù)或則不可刪除的屬性會拋出錯誤;
避免重復(fù)的屬性名和參數(shù)值:對象上重復(fù)的屬性和函數(shù)參數(shù)會拋出錯誤(在ES6中不再是這樣);
使得eval()更加安全:在eval()中定義的變量和函數(shù)在外部作用域不可見;
“安全”的消除JavaScript中this的轉(zhuǎn)換:如果this是null或則undefined不在轉(zhuǎn)換到全局對象。也就是說在瀏覽器中使用this去指向全局對象不再可行。
對于在嚴格(strict)模式和測試階段都沒有發(fā)現(xiàn)的bug,不妨接入線上實時監(jiān)控插件Fundebug。
快速掌握JavaScript面試基礎(chǔ)知識(一) -->
版權(quán)聲明:
轉(zhuǎn)載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/201...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107436.html
摘要:第一部分請點擊快速掌握面試基礎(chǔ)知識一關(guān)鍵字如果使用關(guān)鍵字來調(diào)用函數(shù)式很特別的形式。該對象默認包含了指向原構(gòu)造函數(shù)的屬性。接下來通過例子來幫助理解屬性包含了構(gòu)造函數(shù)以及構(gòu)造函數(shù)中在上定義的屬性。也就是說,的回調(diào)函數(shù)后執(zhí)行。 譯者按: 總結(jié)了大量JavaScript基本知識點,很有用! 原文: The Definitive JavaScript Handbook for your next...
摘要:根據(jù)調(diào)查,自年一來,是最流行的編程語言。在一個函數(shù)體中聲明的變量和函數(shù),周圍的作用域內(nèi)無法訪問。也就是說被大括號包圍起來的區(qū)域聲明的變量外部將不可訪問。一個常見的誤解是使用聲明的變量,其值不可更改。 譯者按: 總結(jié)了大量JavaScript基本知識點,很有用! 原文: The Definitive JavaScript Handbook for your next developer ...
摘要:上周末看這篇文章時,偶有靈光,所以,分享出來給大家一起看看前端面試四月二十家前端面試題分享請各位讀者添加一下作者的微信公眾號,以后有新的文章,將在微信公眾號直接推送給各位,非常感謝。 前端切圖神器 avocode 有了這個神器,切圖再也腰不酸,腿不疼了。 這一次,徹底弄懂 JavaScript 執(zhí)行機制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機制,如果讀完本文還不懂,...
摘要:希望幫助更多的前端愛好者學(xué)習(xí)。前端開發(fā)者指南作者科迪林黎,由前端大師傾情贊助。翻譯最佳實踐譯者張捷滬江前端開發(fā)工程師當你問起有關(guān)與時,老司機們首先就會告訴你其實是個沒有網(wǎng)絡(luò)請求功能的庫。 前端基礎(chǔ)面試題(JS部分) 前端基礎(chǔ)面試題(JS部分) 學(xué)習(xí) React.js 比你想象的要簡單 原文地址:Learning React.js is easier than you think 原文作...
摘要:為此決定自研一個富文本編輯器。本文,主要介紹如何實現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的。 對ES6Generator函數(shù)的理解 Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同。 JavaScript 設(shè)計模式 ② 巧用工廠模式和創(chuàng)建者模式 我為什么把他們兩個放在一起講?我覺得這兩個設(shè)計模式有相似之處,有時候會一個設(shè)計模式不能滿...
閱讀 800·2023-04-25 20:47
閱讀 2566·2019-08-30 15:53
閱讀 975·2019-08-26 14:05
閱讀 923·2019-08-26 11:59
閱讀 1709·2019-08-26 11:43
閱讀 1725·2019-08-26 10:57
閱讀 1383·2019-08-23 18:23
閱讀 2729·2019-08-23 12:57