摘要:閉包是返回另一個(gè)函數(shù)并攜帶數(shù)據(jù)的函數(shù)。當(dāng)程序的上下文和作用域發(fā)生變化時(shí),也會(huì)發(fā)生相應(yīng)的變化。之所以是類型,是因?yàn)轭惖臉?gòu)造函數(shù)它是類型的。如下這里的是一個(gè)回調(diào)函數(shù),當(dāng)成功響應(yīng)請(qǐng)求時(shí)將執(zhí)行該回調(diào)函數(shù)。
譯者:前端小智
原文:medium.com/dev-bits/a-…
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!
為了說明 JS 面試的復(fù)雜性,首先,請(qǐng)嘗試給出以下結(jié)果:
onsole.log(2.0 == “2” == new Boolean(true) == “1”)
十有八九的會(huì)給出false, 其實(shí)運(yùn)行結(jié)果是true,原因請(qǐng)看 這里。
1) 理解 JS 函數(shù)
函數(shù)是 JavaScript 的精華,是 JS 一等公民。JS 函數(shù)不僅僅是一個(gè)普通的函數(shù),與其他語(yǔ)言不同,JS 函數(shù)可以賦值給變量,作為參數(shù)傳遞給另一個(gè)函數(shù),也可以從另一個(gè)函數(shù)返回。
console.log(square(5)); /* ... */ function square(n) { return n * n; }
以為代碼很簡(jiǎn)單,大家應(yīng)該都知道會(huì)打印:25。接著看一個(gè):
console.log(square(5)); var square = function(n) { return n * n; }
乍一看,你可能會(huì)忍不住說也打印了 25。但很不幸,會(huì)報(bào)錯(cuò):
TypeError: square is not a function
在 JavaScript 中,如果將函數(shù)定義為變量,變量名將被提升,是 JS 執(zhí)行到它的定義才能被訪問。
你可能在一些代碼中頻繁的見到如下代碼。
var simpleLibrary = function() { var simpleLibrary = { a, b, add: function(a, b) { return a + b; }, subtract: function(a, b) { return a - b; } } return simpleLibrary; }();
為什么會(huì)做這種奇怪的事情? 這是因?yàn)橐粋€(gè)函數(shù)變量中變量和函數(shù)被分裝,可以避免全局變量污染。 JQuery 到Lodash 的庫(kù)采用這種技術(shù)提供 $、_ 等
2) 理解 bind、apply 和 call
你可能在所有常用庫(kù)中看到過這三個(gè)函數(shù)。它們?cè)试S局部套用, 我們可以把功能組合到不同的函數(shù)。一個(gè)優(yōu)秀的js開發(fā)者可以隨時(shí)告訴你關(guān)于這三個(gè)函數(shù)。
基本上,這些是改變行為以實(shí)現(xiàn)某些功能的原型方法,根據(jù) JS 開發(fā)人員 Chad 的說法,用法如下:
希望使用某個(gè)上下文調(diào)用該函數(shù),請(qǐng)使用 .bind() ,這在事件中很有用。 如果要立即調(diào)用函數(shù),請(qǐng)使用.call() 或 .apply(),并修改上下文。
讓我們看看上面的陳述是什么意思! 假設(shè)你的數(shù)學(xué)老師要求你創(chuàng)建一個(gè)庫(kù)并提交。你寫了一個(gè)抽象的庫(kù),它可以求出圓的面積和周長(zhǎng):
var mathLib = { pi: 3.14, area: function(r) { return this.pi * r * r; }, circumference: function(r) { return 2 * this.pi * r; } };
提交后,老師調(diào)用了它:
mathLib.area(2); 12.56
老師發(fā)現(xiàn)他給你要求是 pi 精確到小數(shù)點(diǎn)后 5 位數(shù)而你只精確到 2 位, 現(xiàn)在由于最后期限已過你沒有機(jī)會(huì)提交庫(kù)。 這里 JS的 call 函數(shù)可以幫你, 只需要調(diào)用你的代碼如下:
mathLib.area.call({pi: 3.1159}, 2)
它會(huì)動(dòng)態(tài)地獲取新的 pi 值,結(jié)果如下:
12.56636
這時(shí),注意到 call 函數(shù)具有兩個(gè)參數(shù):
Context
函數(shù)參數(shù)
在 area 函數(shù)中, 上下文是對(duì)象被關(guān)鍵詞 this 代替,后面的參數(shù)作為函數(shù)參數(shù)被傳遞。 如下:
var cylinder = { pi: 3.14, volume: function(r, h) { return this.pi * r * r * h; } };
調(diào)用方式如下:
cylinder.volume.call({pi: 3.14159}, 2, 6); 75.39815999999999
Apply 類似,只是函數(shù)參數(shù)作為數(shù)組傳遞。
cylinder.volume.apply({pi: 3.14159}, [2, 6]); 75.39815999999999
如果你會(huì)使用 call 你基本就會(huì)用 apply 了,反之亦然, 那 bind 的用法又是如何呢 ?
bind 將一個(gè)全新的 this 注入到指定的函數(shù)上,改變 this 的指向, 使用 bind 時(shí),函數(shù)不會(huì)像 call 或 apply 立即執(zhí)行。
var newVolume = cylinder.volume.bind({pi: 3.14159}); newVolume(2,6); // Now pi is 3.14159
bind 用途是什么");
3) 理解 js 作用域(閉包)
JavaScript 的作用域是一個(gè)潘多拉盒子。從這一個(gè)簡(jiǎn)單的概念中,就可以構(gòu)造出數(shù)百個(gè)難回答的面試問題。有三種作用域:
全局作用域
本地/函數(shù)作用域
塊級(jí)作用域(ES6引進(jìn))
全局作用域事例如下:
x = 10; function Foo() { console.log(x); // Prints 10 } Foo()
函數(shù)作用域生效當(dāng)你定義一個(gè)局部變量時(shí):
pi = 3.14; function circumference(radius) { pi = 3.14159; console.log(2 * pi * radius); // 打印 "12.56636" 不是 "12.56" } circumference(2);
ES16 標(biāo)準(zhǔn)引入了新的塊作用域,它將變量的作用域限制為給定的括號(hào)塊。
var a = 10; function Foo() { if (true) { let a = 4; } alert(a); // alerts "10" because the "let" keyword } Foo();
函數(shù)和條件都被視為塊。以上例子應(yīng)該彈出 4,因?yàn)?if 已執(zhí)行。但 是ES6 銷毀了塊級(jí)變量的作用域,作用域進(jìn)入全局。
現(xiàn)在來(lái)到神奇的作用域,可以使用閉包來(lái)實(shí)現(xiàn),JavaScript 閉包是一個(gè)返回另一個(gè)函數(shù)的函數(shù)。
如果有人問你這個(gè)問題,編寫一個(gè)輸入一個(gè)字符串并逐次返回字符。 如果給出了新字符串,則應(yīng)該替換舊字符串,類似簡(jiǎn)單的一個(gè)生成器。
function generator(input) { var index = 0; return { next: function() { if (index < input.length) { index += 1; return input[index - 1]; } return ""; } } }
執(zhí)行如下:
var mygenerator = generator("boomerang"); mygenerator.next(); // returns "b" mygenerator.next() // returns "o" mygenerator = generator("toon"); mygenerator.next(); // returns "t"
在這里,作用域扮演著重要的角色。閉包是返回另一個(gè)函數(shù)并攜帶數(shù)據(jù)的函數(shù)。上面的字符串生成器適用于閉包。index 在多個(gè)函數(shù)調(diào)用之間保留,定義的內(nèi)部函數(shù)可以訪問在父函數(shù)中定義的變量。這是一個(gè)不同的作用域。如果在第二級(jí)函數(shù)中再定義一個(gè)函數(shù),它可以訪問所有父級(jí)變量。
4) this (全局域、函數(shù)域、對(duì)象域)
在 JavaScript 中,我們總是用函數(shù)和對(duì)象編寫代碼, 如果使用瀏覽器,則在全局上下文中它引用 window 對(duì)象。 我的意思是,如果你現(xiàn)在打開瀏覽器控制臺(tái)并輸入以下代碼,輸出結(jié)果為 true。
this === window;
當(dāng)程序的上下文和作用域發(fā)生變化時(shí),this 也會(huì)發(fā)生相應(yīng)的變化?,F(xiàn)在觀察 this 在一個(gè)局部上下文中:
function Foo(){ console.log(this.a); } var food = {a: "Magical this"}; Foo.call(food); // food is this
思考一下,以下輸出的是什么:
function Foo(){ console.log(this); // 打印 {}");
因?yàn)檫@是一個(gè)全局對(duì)象,記住,無(wú)論父作用域是什么,它都將由子作用域繼承。打印出來(lái)是 window 對(duì)象。上面討論的三個(gè)方法實(shí)際上用于設(shè)置這個(gè)對(duì)象。
現(xiàn)在,this 的最后一個(gè)類型,在對(duì)象中的 this, 如下:
var person = { name: "Stranger", age: 24, get identity() { return {who: this.name, howOld: this.age}; } }
上述使用了 getter 語(yǔ)法,這是一個(gè)可以作為變量調(diào)用的函數(shù)。
person.identity; // returns {who: "Stranger", howOld: 24}
此時(shí),this 實(shí)際上是指對(duì)象本身。正如我們前面提到的,它在不同的地方有不同的表現(xiàn)。
5) 理解對(duì)象 (Object.freeze, Object.seal)
通常對(duì)象的格式如下:
var marks = {physics: 98, maths:95, chemistry: 91};
它是一個(gè)存儲(chǔ)鍵、值對(duì)的映射。 javascript 對(duì)象有一個(gè)特殊的屬性,可以將任何東西存儲(chǔ)為一個(gè)值。這意味著我們可以將一個(gè)列表、另一個(gè)對(duì)象、一個(gè)函數(shù)等存儲(chǔ)為一個(gè)值。
可以用如下方式來(lái)創(chuàng)建對(duì)象:
var marks = {}; var marks = new Object();
可以使用 JSON.stringify() 將一個(gè)對(duì)象轉(zhuǎn)制成字符串,也可以用 JSON.parse 在將其轉(zhuǎn)成對(duì)象。
// returns "{"physics":98,"maths":95,"chemistry":91}" JSON.stringify(marks); // Get object from string JSON.parse("{"physics":98,"maths":95,"chemistry":91}");
使用 Object.keys 迭代對(duì)象:
var highScere = 0; for (i of Object.keys(marks)) { if (marks[i] > highScore) highScore = marks[i]; }
Object.values 以數(shù)組的方式返回對(duì)象的值。
對(duì)象上的其他重要函數(shù)有:
Object.prototype(object)
Object.freeze(function)
Object.seal(function)
Object.prototype 上提供了許多應(yīng)用上相關(guān)的函數(shù),如下:
Object.prototype.hasOwnProperty 用于檢查給定的屬性/鍵是否存在于對(duì)象中。
marks.hasOwnProperty("physics"); // returns true marks.hasOwnProperty("greek"); // returns false
Object.prototype.instanceof 判斷給定對(duì)象是否是特定原型的類型。
function Car(make, model, year) { this.make = make; this.model = model; this.year = year; } var newCar = new Car("Honda", "City", 2007); console.log(newCar instanceof Car); // returns true
使用 Object.freeze 可以凍結(jié)對(duì)象,以便不能修改對(duì)象現(xiàn)有屬性。
var marks = {physics: 98, maths:95, chemistry: 91}; finalizedMarks = Object.freeze(marks); finalizedMarks["physics"] = 86; // throws error in strict mode console.log(marks); // {physics: 98, maths: 95, chemistry: 91}
在這里,試圖修改凍結(jié)后的 physics 的值,但 JavaScript不允許這樣做。我們可以使用 Object.isFrozen 來(lái)判斷,給定對(duì)象是否被凍結(jié):
Object.isFrozen(finalizedMarks); // returns true
Object.seal 與 Object.freeze 略有不同。 Object.seal() 方法封閉一個(gè)對(duì)象,阻止添加新屬性并將所有現(xiàn)有屬性標(biāo)記為不可配置。當(dāng)前屬性的值只要可寫就可以改變。
var marks = {physics: 98, maths:95, chemistry: 91}; Object.seal(marks); delete marks.chemistry; // returns false as operation failed marks.physics = 95; // Works! marks.greek = 86; // Will not add a new property
同樣, 可以使用 Object.isSealed 判斷對(duì)象是否被密封。
Object.isSealed(marks); // returns true
在全局對(duì)象函數(shù)上還有許多其他重要的函數(shù)/方法,在這里找到他們。
6) 理解原型繼承
在傳統(tǒng) JavaScript 中,有一種偽裝的繼承概念,它是通過使用原型技術(shù)來(lái)實(shí)現(xiàn)的。在ES5、ES6中看到使用 new 的語(yǔ)法只是底層原型OOP的語(yǔ)法糖。創(chuàng)建類是使用 JavaScript 中的函數(shù)完成的。
var animalGroups = { MAMMAL: 1, REPTILE: 2, AMPHIBIAN: 3, INVERTEBRATE: 4 }; function Animal(name, type) { this.name = name; this.type = type; } var dog = new Animal("dog", animalGroups.MAMMAL); var crocodile = new Animal("crocodile", animalGroups.REPTILE);
這里我們?yōu)轭悇?chuàng)建對(duì)象(使用 new 關(guān)鍵字),可以使用如下方式對(duì)類追加方法:
Animal.prototype.shout = function() { console.log(this.name+"is"+this.sound+"ing..."); }
這里你可能會(huì)有疑問。類中并沒 sound 屬性。是的,它打算由繼承了上述類的子類傳遞。
JavaScript中, 如下實(shí)現(xiàn)繼承:
function Dog(name, type) { Animal.call(this, name, type); this.sound = "bow"; }
我定義了一個(gè)更具體的函數(shù),叫做 Dog。在這里,為了繼承 Animal 類,我需要call傳遞this和其他參數(shù)。使用如下方式來(lái)實(shí)例化一只德國(guó)牧羊犬。
var pet = Dog("德國(guó)牧羊犬", animalGroups.MAMMAL); console.log(pet); // returns Dog {name: "德國(guó)牧羊犬", type: 1, sound: "bow"}
我們沒有在子函數(shù)中分配 name 和 type 屬性,我們調(diào)用的是超級(jí)函數(shù) Animal 并設(shè)置相應(yīng)的屬性。pet 具有父類的屬性(name、type)。但是方法呢。他們也繼承的嗎");
pet.shout(); // Throws error
為什么會(huì)這樣? 之所以發(fā)生這種情況,是因?yàn)闆]有指定讓 JavaScript來(lái)繼承父類方法。 如何解決?
// Link prototype chains Dog.prototype = Object.create(Animal.prototype); var pet = new Dog("germanShepard", animalGroups.MAMMAL); // Now shout method is available pet.shout(); // 德國(guó)牧羊犬 bowing...
現(xiàn)在可以使用 shout 方法。 我們可以使用 object.constructor 函數(shù)檢查 JavaScript 中給定對(duì)象的類 來(lái)看看 pet 是什么類:
pet.constructor; // returns Animal
這是模糊的,Animal 是一個(gè)父類。但是 pet 到底是什么類型的呢");pet 應(yīng)該是 Dog 的類型。之所以是 Animal 類型,是因?yàn)?Dog 類的構(gòu)造函數(shù):
Dog.prototype.constructor; // returns Animal
它是 Animal 類型的。我們應(yīng)該將它設(shè)置為 Dog 本身,這樣類的所有實(shí)例(對(duì)象)才能給出正確的類名。
Dog.prototype.constructor = Dog;
關(guān)于原型繼承, 我們應(yīng)該記住以下幾條:
類屬性使用 this 綁定
類方法使用 prototype 對(duì)象來(lái)綁定
為了繼承屬性, 使用 call 函數(shù)來(lái)傳遞 this
為了繼承方法, 使用 Object.create 連接父和子的原型
始終將子類構(gòu)造函數(shù)設(shè)置為自身,以獲得其對(duì)象的正確類型
7)理解 callback 和 promise
回調(diào)是在 I/O 操作完成后執(zhí)行的函數(shù)。一個(gè)耗時(shí)的I/O操作會(huì)阻塞代碼, 因此在Python/Ruby不被允許。但是在 JavaScript中,由于允許異步執(zhí)行,我們可以提供對(duì)異步函數(shù)的回調(diào)。這個(gè)例子是由瀏覽器到服務(wù)器的AJAX(XMLHettpRequest)調(diào)用,由鼠標(biāo)、鍵盤事件生成。如下:
function reqListener () { console.log(this.responseText); } var req = new XMLHttpRequest(); req.addEventListener("load", reqListener); req.open("GET", "http://www.example.org/example.txt"); req.send();
這里的 reqListener 是一個(gè)回調(diào)函數(shù),當(dāng)成功響應(yīng) GET 請(qǐng)求時(shí)將執(zhí)行該回調(diào)函數(shù)。
Promise 是回調(diào)函數(shù)的優(yōu)雅的封裝, 使得我們優(yōu)雅的實(shí)現(xiàn)異步代碼。在以下給出的這篇文章中討論了很多 promise,這也是在 JS 中應(yīng)該知道的重要部分。
Writing neat asynchronous Node JS code with Promises
8)理解正則表達(dá)
正則表達(dá)式有許多應(yīng)用地方,處理文本、對(duì)用戶輸入執(zhí)行規(guī)則等。JavaScript 開發(fā)人員應(yīng)該知道如何執(zhí)行基本正則表達(dá)式并解決問題。Regex 是一個(gè)通用概念,來(lái)看看如何從 JS 中做到這一點(diǎn)。
創(chuàng)建正則表達(dá)式,有如下兩種方式:
var re = /ar/; var re = new RegExp("ar");
上面的正則表達(dá)式是與給定字符串集匹配的表達(dá)式。定義正則表達(dá)式之后,我們可以嘗試匹配并查看匹配的字符串??梢允褂?exec 函數(shù)匹配字符串:
re.exec("car"); // returns ["ar", index: 1, input: "car"] re.exec("cab"); // returns null
有一些特殊的字符類允許我們編寫復(fù)雜的正則表達(dá)式。RegEx 中有許多類型的元素,其中一些如下:
字符正則:w-字母數(shù)字, d- 數(shù)字, D- 沒有數(shù)字
字符類正則:[x-y] x-y區(qū)間, [^x] 沒有x
數(shù)量正則:+ 至少一個(gè)、"); 沒或多個(gè)、* 多個(gè)
邊界正則,^ 開始、$ 結(jié)尾
例子如下:
/* Character class */ var re1 = /[AEIOU]/; re1.exec("Oval"); // returns ["O", index: 0, input: "Oval"] re1.exec("2456"); // null var re2 = /[1-9]/; re2.exec("mp4"); // returns ["4", index: 2, input: "mp4"] /* Characters */ var re4 = /dDw/; re4.exec("1232W2sdf"); // returns ["2W2", index: 3, input: "1232W2sdf"] re4.exec("W3q"); // returns null /* Boundaries */ var re5 = /^dDw/; re5.exec("2W34"); // returns ["2W3", index: 0, input: "2W34"] re5.exec("W34567"); // returns null var re6 = /^[0-9]{5}-[0-9]{5}-[0-9]{5}$/; re6.exec("23451-45242-99078"); // returns ["23451-45242-99078", index: 0, input: "23451-45242-99078"] re6.exec("23451-abcd-efgh-ijkl"); // returns null /* Quantifiers */ var re7 = /d+D+$/; re7.exec("2abcd"); // returns ["2abcd", index: 0, input: "2abcd"] re7.exec("23"); // returns null re7.exec("2abcd3"); // returns null var re8 = /<([w]+).*>(.*");
有關(guān) regex 的詳細(xì)信息,可以看 這里。
除了 exec 之外,還有其他函數(shù),即 match、search 和 replace,可以使用正則表達(dá)式在另一個(gè)字符串中查找字符串,但是這些函數(shù)在字符串本身上使用。
"2345-678r9".match(/[a-z A-Z]/); // returns ["r", index: 8, input: "2345-678r9"] "2345-678r9".replace(/[a-z A-Z]/, ""); // returns 2345-6789
Regex 是一個(gè)重要的主題,開發(fā)人員應(yīng)該理解它,以便輕松解決復(fù)雜的問題。
9)理解 map、reduce 和 filter
函數(shù)式編程是當(dāng)今的一個(gè)熱門討論話題。許多編程語(yǔ)言都在新版本中包含了函數(shù)概念,比如 lambdas(例如:Java >7)。在 JavaScrip t中,函數(shù)式編程結(jié)構(gòu)的支持已經(jīng)存在很長(zhǎng)時(shí)間了。我們需要深入學(xué)習(xí)三個(gè)主要函數(shù)。數(shù)學(xué)函數(shù)接受一些輸入和返回輸出。純函數(shù)都是給定的輸入返回相同的輸出。我們現(xiàn)在討論的函數(shù)也滿足純度。
map
map 函數(shù)在 JavaScript 數(shù)組中可用,使用這個(gè)函數(shù),我們可以通過對(duì)數(shù)組中的每個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換函數(shù)來(lái)獲得一個(gè)新的數(shù)組。map 一般語(yǔ)法是:
arr.map((elem){ process(elem) return processedValue }) // returns new array with each element processed
假設(shè),在我們最近使用的串行密鑰中輸入了一些不需要的字符,需要移除它們。此時(shí)可以使用 map 來(lái)執(zhí)行相同的操作并獲取結(jié)果數(shù)組,而不是通過迭代和查找來(lái)刪除字符。
var data = ["2345-34r", "2e345-211", "543-67i4", "346-598"]; var re = /[a-z A-Z]/; var cleanedData = data.map((elem) => {return elem.replace(re, "")}); console.log(cleanedData); // ["2345-34", "2345-211", "543-674", "346-598"]
map 接受一個(gè)作為參數(shù)的函數(shù), 此函數(shù)接受一個(gè)來(lái)自數(shù)組的參數(shù)。我們需要返回一個(gè)處理過的元素, 并應(yīng)用于數(shù)組中的所有元素。
reduce
reduce 函數(shù)將一個(gè)給定的列表整理成一個(gè)最終的結(jié)果。通過迭代數(shù)組執(zhí)行相同的操作, 并保存中間結(jié)果到一個(gè)變量中。這里是一個(gè)更簡(jiǎn)潔的方式進(jìn)行處理。js 的 reduce 一般使用語(yǔ)法如下:
arr.reduce((accumulator, currentValue, currentIndex) => { process(accumulator, currentValue) return intermediateValue/finalValue }, initialAccumulatorValue) // returns reduced value
accumulator 存儲(chǔ)中間值和最終值。currentIndex、currentValue分別是數(shù)組中元素的 index 和 value。initialAccumulatorValue 是 accumulator 初始值。
reduce 的一個(gè)實(shí)際應(yīng)用是將一個(gè)數(shù)組扁平化, 將內(nèi)部數(shù)組轉(zhuǎn)化為單個(gè)數(shù)組, 如下:
var arr = [[1, 2], [3, 4], [5, 6]]; var flattenedArray = [1, 2, 3, 4, 5, 6];
我們可以通過正常的迭代來(lái)實(shí)現(xiàn)這一點(diǎn),但是使用 reduce,代碼會(huì)更加簡(jiǎn)潔。
var flattenedArray = arr.reduce((accumulator, currentValue) => { return accumulator.concat(currentValue); }, []); // returns [1, 2, 3, 4, 5, 6]
filter
filter 與 map 更為接近, 對(duì)數(shù)組的每個(gè)元素進(jìn)行操作并返回另外一個(gè)數(shù)組(不同于 reduce 返回的值)。過濾后的數(shù)組可能比原數(shù)組長(zhǎng)度更短,因?yàn)橥ㄟ^過濾條件,排除了一些我們不需要的。
filter 語(yǔ)法如下:
arr.filter((elem) => { return true/false })
elem 是數(shù)組中的元素, 通過 true/false 表示過濾元素保存/排除。假設(shè), 我們過濾出以 t 開始以 r 結(jié)束的元素:
var words = ["tiger", "toast", "boat", "tumor", "track", "bridge"] var newData = words.filter((str) => { return str.startsWith("t") && str.endsWith("r"); }) newData // (2) ["tiger", "tumor"]
當(dāng)有人問起JavaScript的函數(shù)編程方面時(shí),這三個(gè)函數(shù)應(yīng)該信手拈來(lái)。 如你所見,原始數(shù)組在所有三種情況下都沒有改變,這證明了這些函數(shù)的純度。
10) 理解錯(cuò)誤處理模式
這是許多開發(fā)人員最不關(guān)心的 JavaScript。 我看到很少有開發(fā)人員談?wù)撳e(cuò)誤處理, 一個(gè)好的開發(fā)方法總是謹(jǐn)慎地將 JS 代碼封裝裝在 try/catch 塊周圍。
在 JavaScript中,只要我們隨意編寫代碼,就可能會(huì)失敗,如果所示:
$("button").click(function(){ $.ajax({url: "user.json", success: function(result){ updateUI(result["posts"]); }}); });
這里,我們陷入了一個(gè)陷阱,我們說 result 總是 JSON 對(duì)象。但有時(shí)服務(wù)器會(huì)崩潰,返回的是 null 而不是 result。在這種情況下,null["posts"] 將拋出一個(gè)錯(cuò)誤。正確的處理方式可能是這樣的:
$("button").click(function(){ $.ajax({url: "user.json", success: function(result){ try { updateUI(result["posts"]); } catch(e) { // Custom functions logError(); flashInfoMessage(); } }}); });
logError 函數(shù)用于向服務(wù)器報(bào)告錯(cuò)誤。flashInfoMessage 是顯示用戶友好的消息,如“當(dāng)前不可用的服務(wù)”等。
Nicholas 說,當(dāng)你覺得有什么意想不到的事情將要發(fā)生時(shí),手動(dòng)拋出錯(cuò)誤。區(qū)分致命錯(cuò)誤和非致命錯(cuò)誤。以上錯(cuò)誤與后端服務(wù)器宕機(jī)有關(guān),這是致命的。在那里,應(yīng)該通知客戶由于某種原因服務(wù)中斷了。
在某些情況下,這可能不是致命的,但最好通知服務(wù)器。為了創(chuàng)建這樣的代碼,首先拋出一個(gè)錯(cuò)誤,, 從 window 層級(jí)捕捉錯(cuò)誤事件,然后調(diào)用API將該消息記錄到服務(wù)器。
reportErrorToServer = function (error) { $.ajax({type: "POST", url: "http://api.xyz.com/report", data: error, success: function (result) {} }); } // Window error event window.addEventListener("error", function (e) { reportErrorToServer({message: e.message}) })} function mainLogic() { // Somewhere you feel like fishy throw new Error("user feeds are having fewer fields than expected..."); }
這段代碼主要做三件事:
監(jiān)聽window層級(jí)錯(cuò)誤
無(wú)論何時(shí)發(fā)生錯(cuò)誤,都要調(diào)用 API
在服務(wù)器中記錄
你也可以使用新的 Boolean 函數(shù)(es5,es6)在程序之前監(jiān)測(cè)變量的有效性并且不為null、undefined
if (Boolean(someVariable)) { // use variable now } else { throw new Error("Custom message") }
始終考慮錯(cuò)誤處理是你自己, 而不是瀏覽器。
其他(提升機(jī)制和事件冒泡)
以上所有概念都是 JavaScript 開發(fā)人員的需要知道基本概念。有一些內(nèi)部細(xì)節(jié)需要知道,這些對(duì)你會(huì)有很在幫助。 這些是JavaScript引擎在瀏覽器中的工作方式,什么是提升機(jī)制和事件冒泡?
提升機(jī)制
變量提升是 在代碼執(zhí)行過程中將聲明的變量的作用域提升到全局作用哉中的一個(gè)過程,如:
doSomething(foo); // used before var foo; // declared later
當(dāng)在 Python 這樣的腳本語(yǔ)言中執(zhí)行上述操作時(shí),它會(huì)拋出一個(gè)錯(cuò)誤,因?yàn)樾枰榷x然后才能使用它。盡管 JS 是一種腳本語(yǔ)言,但它有一種提升機(jī)制,在這種機(jī)制中,JavaScript VM 在運(yùn)行程序時(shí)做兩件事:
首先掃描程序,收集所有的變量和函數(shù)聲明,并為其分配內(nèi)存空間
通過填充分配的變量來(lái)執(zhí)行程序, 沒有分配則填充 undefined
在上面的代碼片段中,console.log 打印 “undefined”。 這是因?yàn)樵诘谝淮蝹鬟f變量 foo 被收集。 JS 虛擬機(jī) 查找為變量 foo 定義的任何值。 這種提升可能導(dǎo)致許多JavaScript 在某些地方拋出錯(cuò)誤,和另外地方使用 undefined 。
學(xué)習(xí)一些 例子 來(lái)搞清楚提升。
事件冒泡
現(xiàn)在事件開始冒泡了! 根據(jù)高級(jí)軟件工程師 Arun P的說法:
“當(dāng)事件發(fā)生在另一個(gè)元素內(nèi)的元素中時(shí),事件冒泡和捕獲是 HTML DOM API 中事件傳播的兩種方式,并且這兩個(gè)元素都已為該事件注冊(cè)了處理程序,事件傳播模式確定元素接收事件的順序。“
通過冒泡,事件首先由最內(nèi)部的元素捕獲和處理,然后傳播到外部元素。對(duì)于捕獲,過程是相反的。我們通常使用addEventListener 函數(shù)將事件附加到處理程序。
addEventListener("click", handler, useCapture=false)
useCapture 是第三個(gè)參數(shù)的關(guān)鍵詞, 默認(rèn)為 false。因此, 冒泡模式是事件由底部向上傳遞。 反之, 這是捕獲模式。
冒泡模式:
點(diǎn)擊li元素, 事件順序:
handler() => ulHandler() => divHandler()
在圖中,處理程序按順序向外觸發(fā)。類似地,捕獲模型試圖將事件從父元素向內(nèi)觸發(fā)到單擊的元素?,F(xiàn)在更改上面代碼中的這一行。
document.getElementById("foo").addEventListener("click", handler, true)
事件順序:
divHandler => ulHandler() => handler()
你應(yīng)該正確地理解事件冒泡(無(wú)論方向是指向父節(jié)點(diǎn)還是子節(jié)點(diǎn)),以實(shí)現(xiàn)用戶界面(UI),以避免任何不需要的行為。
這些是 JavaScrip t中的基本概念。正如我最初提到的,除了工作經(jīng)驗(yàn)和知識(shí)之外,準(zhǔn)備有助理于你通過 JavaScript 面試。始終保持學(xué)習(xí)。留意最新的發(fā)展(第六章)。深入了解JavaScript的各個(gè)方面,如 V6 引擎、測(cè)試等。最后,沒有掌握數(shù)據(jù)結(jié)構(gòu)和算法的面試是不成功的。Oleksii Trekhleb 策劃了一個(gè)很棒的 git repo,它包含了所有使用 JS 代碼的面試準(zhǔn)備算法。
代碼部署后可能存在的BUG沒法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。
原文:
medium.com/dev-bits/a-…
你的點(diǎn)贊是我持續(xù)分享好東西的動(dòng)力,歡迎點(diǎn)贊!
####歡迎加入前端大家庭,里面會(huì)經(jīng)常分享一些技術(shù)資源。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/6708.html
摘要:閉包是返回另一個(gè)函數(shù)并攜帶數(shù)據(jù)的函數(shù)。當(dāng)程序的上下文和作用域發(fā)生變化時(shí),也會(huì)發(fā)生相應(yīng)的變化。之所以是類型,是因?yàn)轭惖臉?gòu)造函數(shù)它是類型的。如下這里的是一個(gè)回調(diào)函數(shù),當(dāng)成功響應(yīng)請(qǐng)求時(shí)將執(zhí)行該回調(diào)函數(shù)。 showImg(https://segmentfault.com/img/bVboyxw?w=2560&h=1440); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!...
摘要:雖然有著各種各樣的不同,但是相同的是,他們前端優(yōu)化不完全指南前端掘金篇幅可能有點(diǎn)長(zhǎng),我想先聊一聊閱讀的方式,我希望你閱讀的時(shí)候,能夠把我當(dāng)作你的競(jìng)爭(zhēng)對(duì)手,你的夢(mèng)想是超越我。 如何提升頁(yè)面渲染效率 - 前端 - 掘金Web頁(yè)面的性能 我們每天都會(huì)瀏覽很多的Web頁(yè)面,使用很多基于Web的應(yīng)用。這些站點(diǎn)看起來(lái)既不一樣,用途也都各有不同,有在線視頻,Social Media,新聞,郵件客戶端...
摘要:哈哈,我理解,架構(gòu)就是骨架,如下圖所示譯年月個(gè)有趣的和庫(kù)前端掘金我們創(chuàng)辦的使命是讓你及時(shí)的了解開發(fā)中最新最酷的趨勢(shì)。 翻譯 | 上手 Webpack ? 這篇就夠了! - 掘金譯者:小 boy (滬江前端開發(fā)工程師) 本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處。 原文地址:https://www.smashingmagazine.... JavaSrip... 讀 Zepto 源碼之代碼結(jié)構(gòu) - ...
摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...
閱讀 2536·2021-10-11 10:59
閱讀 2715·2021-09-22 15:49
閱讀 2650·2021-08-13 13:25
閱讀 1293·2019-08-30 13:14
閱讀 2396·2019-08-29 18:45
閱讀 3003·2019-08-29 18:36
閱讀 1495·2019-08-29 13:21
閱讀 1166·2019-08-26 11:44