👇 內容速覽 👇javascript
題目:ES5中經常使用繼承方法。
方法一:綁定構造函數前端
缺點:不能繼承父類原型方法/屬性java
function Animal(){ this.species = '動物' } function Cat(){ // 執行父類的構造方法, 上下文爲實例對象 Animal.apply(this, arguments) } /** * 測試代碼 */ var cat = new Cat() console.log(cat.species) // output: 動物
方法二:原型鏈繼承node
缺點:沒法向父類構造函數中傳遞參數;子類原型鏈上定義的方法有前後順序問題。webpack
注意:js中交換原型鏈,均須要修復prototype.constructor
指向問題。css3
function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){} /** * func方法是無效的, 由於後面原型鏈被從新指向了Animal實例 */ Cat.prototype.func = function() { console.log('Cat') } Cat.prototype = new Animal() Cat.prototype.constructor = Cat // 修復: 將Cat.prototype.constructor從新指向自己 /** * 測試代碼 */ var cat = new Cat() cat.func() // output: Animal console.log(cat.species) // undefined
方法3:組合繼承git
結合綁定構造函數和原型鏈繼承2種方式,缺點是:調用了2次父類的構造函數。es6
function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){ Animal.apply(this, arguments) } Cat.prototype = new Animal() Cat.prototype.constructor = Cat /** * 測試代碼 */ var cat = new Cat('cat') cat.func() // output: Animal console.log(cat.species) // output: cat
方法4:寄生組合繼承github
改進了組合繼承的缺點,只須要調用1次父類的構造函數。它是引用類型最理想的繼承範式。(引自:《JavaScript高級程序設計》)
/** * 寄生組合繼承的核心代碼 * @param {Function} sub 子類 * @param {Function} parent 父類 */ function inheritPrototype(sub, parent) { // 拿到父類的原型 var prototype = Object(parent.prototype) // 改變constructor指向 prototype.constructor = sub // 父類原型賦給子類 sub.prototype = prototype } function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){ Animal.apply(this, arguments) // 只調用了1次構造函數 } inheritPrototype(Cat, Animal) /** * 測試代碼 */ var cat = new Cat('cat') cat.func() // output: Animal console.log(cat.species) // output: cat
__proto__
屬性,__proto__
屬性值指向它的構造函數的prototype屬性值注:ES6的箭頭函數沒有prototype
屬性,可是有__proto__
屬性。
const obj = {}; // 引用類型的 __proto__ 屬性值指向它的構造函數的 prototype 屬性值 console.log(obj.__proto__ === Object.prototype); // output: true
題目:如何JS中的原型?
// 構造函數 function Foo(name, age) { this.name = name } Foo.prototype.alertName = function () { alert(this.name) } // 建立示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } // 測試 f.printName() f.alertName()
可是執行alertName
時發生了什麼?這裏再記住一個重點 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的__proto__
(即它的構造函數的prototype
)中尋找,所以f.alertName
就會找到Foo.prototype.alertName
。
題目:如何JS中的原型鏈?
以上一題爲基礎,若是調用f.toString()
。
f
試圖從__proto__
中尋找(即Foo.prototype
),仍是沒找到toString()
方法。f.__proto__.__proto__
中尋找(即Foo.prototype.__proto__
中)。由於Foo.prototype
就是一個普通對象,所以Foo.prototype.__proto__ = Object.prototype
Object.prototype.toString
這是對深度遍歷的過程,尋找的依據就是一個鏈式結構,因此叫作「原型鏈」。
題目:如何理解 JS 的做用域和做用域鏈。
①做用域
ES5有」全局做用域「和」函數做用域「。ES6的let
和const
使得JS用了」塊級做用域「。
爲了解決ES5的全局衝突,通常都是閉包編寫:(function(){ ... })()
。將變量封裝到函數做用域。
②做用域鏈
當前做用域沒有找到定義,繼續向父級做用域尋找,直至全局做用域。這種層級關係,就是做用域鏈。
題目:講解下面代碼的執行過程和結果。
var a = true; setTimeout(function(){ a = false; }, 100) while(a){ console.log('while執行了') }
這段代碼會一直執行而且輸出"while..."。JS是單線程的,先跑執行棧裏的同步任務,而後再跑任務隊列的異步任務。
題目:說一下JS的Event Loop。
簡單總結以下:
Loop
。題目:解釋下「全局執行上下文「和「函數執行上下文」。
①全局執行上下文
解析JS時候,建立一個 全局執行上下文 環境。把代碼中即將執行的(內部函數的不算,由於你不知道函數什麼時候執行)變量、函數聲明都拿出來。未賦值的變量就是undefined
。
下面這段代碼輸出:undefined
;而不是拋出Error
。由於在解析JS的時候,變量a已經存入了全局執行上下文中了。
console.log(a); var a = 1;
②函數執行上下文
和全局執行上下文差很少,可是多了this
和arguments
和參數。
在JS中,this
是關鍵字,它做爲內置變量,其值是在執行的時候肯定(不是定義的時候肯定)。
題目:解釋下js的閉包
直接上MDN的解釋:閉包是函數和聲明該函數的詞法環境的組合。
而在JavaScript中,函數是被做爲一級對象使用的,它既能夠本看成值返回,還能夠看成參數傳遞。理解了:「Js中的函數運行在它們被定義的做用域,而不是它們被執行的做用域」(摘自《JavaScript語言精粹》) 這句話便可。
題目:閉包優缺點
閉包封住了變量做用域,有效地防止了全局污染;但同時,它也存在內存泄漏的風險:
解決方法是顯式對外暴露一個接口,專門用以清理變量:
function mockData() { const mem = {} return { clear: () => mem = null, // 顯式暴露清理接口 get: (page) => { if(page in mem) { return mem[page] } mem[page] = Math.random() } } }
《前端知識體系》
《設計模式手冊》
《Webpack4漸進式教程》