您好,登錄后才能下訂單哦!
JS關(guān)于高頻題 原型與原型鏈的面試?針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
國際慣例,讓我們先拋出問題:
或許你已經(jīng)有答案,或許你開始有點疑惑,無論是 get
新技能或是簡單的溫習(xí)一次,讓我們一起去探究一番吧
如果文章中有出現(xiàn)紕漏、錯誤之處,還請看到的小伙伴多多指教,先行謝過
JavaScript
是基于原型的我們創(chuàng)建的每個函數(shù)都有一個
prototype(原型)
屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
簡單來說,就是當(dāng)我們創(chuàng)建一個函數(shù)的時候,系統(tǒng)就會自動分配一個 prototype
屬性,可以用來存儲可以讓所有實例共享的屬性和方法
用一張圖來表示就更加清晰了:
圖解:
prototype
屬性,這個屬性指向一個對象,也就是原型對象constructor
屬性,指向指向它的那個構(gòu)造函數(shù)__proto__
,指向它的原型對象function Person(){} var p = new Person(); p.__proto__ === Person.prototype // true Person.prototype.constructor === Person // true
那么,原型對象都有哪些特點呢
function Person(){} Person.prototype.name = 'tt'; Person.prototype.age = 18; Person.prototype.sayHi = function() { alert('Hi'); } var person1 = new Person(); var person2 = new Person(); person1.name = 'oo'; person1.name // oo person1.age // 18 perosn1.sayHi() // Hi person2.age // 18 person2.sayHi() // Hi
從這段代碼我們不難看出:
既然原型也是對象,那我們可不可以重寫這個對象呢?答案是肯定的
function Person() {} Person.prototype = { name: 'tt', age: 18, sayHi() { console.log('Hi'); } } var p = new Person()
只是當(dāng)我們在重寫原型鏈的時候需要注意以下的問題:
function Person(){} var p = new Person(); Person.prototype = { name: 'tt', age: 18 } Person.prototype.constructor === Person // false p.name // undefined
一圖勝過千言萬語
constructor
屬性指向 Object
,導(dǎo)致原型鏈關(guān)系混亂,所以我們應(yīng)該在重寫原型對象的時候指定 constructor
( instanceof
仍然會返回正確的值)Person.prototype = { constructor: Person }
注意:以這種方式重設(shè) constructor
屬性會導(dǎo)致它的 Enumerable
特性被設(shè)置成 true
(默認(rèn)為false
)
既然現(xiàn)在我們知道了什么是 prototype(原型)
以及它的特點,那么原型鏈又是什么呢?
JavaScript
中所有的對象都是由它的原型對象繼承而來。而原型對象自身也是一個對象,它也有自己的原型對象,這樣層層上溯,就形成了一個類似鏈表的結(jié)構(gòu),這就是原型鏈
同樣的,我們使用一張圖來描述
Object
函數(shù)的 prototype
屬性Objec.prototype
指向的原型對象同樣擁有原型,不過它的原型是 null
,而 null
則沒有原型清楚了原型鏈的概念,我們就能更清楚地知道屬性的查找規(guī)則,比如前面的 p
實例屬性.如果自身和原型鏈上都不存在這個屬性,那么屬性最終的值就是 undefined
,如果是方法就會拋出錯誤
ES6
提供了Class(類)
這個概念,作為對象的模板,通過class
關(guān)鍵字,可以定義類
為什么會提到 class
:
ES6
的 class
可以看作只是一個語法糖,它的絕大部分功能,ES5
都可以做到,新的 class
寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } // 可以這么改寫 function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; };
class
里面定義的方法,其實都是定義在構(gòu)造函數(shù)的原型上面實現(xiàn)實例共享,屬性定義在構(gòu)造函數(shù)中,所以 ES6
中的類完全可以看作構(gòu)造函數(shù)的另一種寫法
除去 class
類中的一些行為可能與 ES5
存在一些不同,本質(zhì)上都是通過原型、原型鏈去定義方法、實現(xiàn)共享。所以,還是文章開始那句話 JavaScript是基于原型的
更多 class
問題,參考這里
instanceof
最常用的確定原型指向關(guān)系的關(guān)鍵字,檢測的是原型,但是只能用來判斷兩個對象是否屬于實例關(guān)系, 而不能判斷一個對象實例具體屬于哪種類型
function Person(){} var p = new Person(); p instanceof Person // true p instanceof Object // true
hasOwnProperty
通過使用 hasOwnProperty
可以確定訪問的屬性是來自于實例還是原型對象
function Person() {} Person.prototype = { name: 'tt' } var p = new Person(); p.age = 15; p.hasOwnProperty('age') // true p.hasOwnProperty('name') // false
由于原型鏈的存在,我們可以讓很多實例去共享原型上面的方法和屬性,方便了我們的很多操作。但是原型鏈并非是十分完美的
function Person(){} Person.prototype.arr = [1, 2, 3, 4]; var person1 = new Person(); var person2 = new Person(); person1.arr.push(5) person2.arr // [1, 2, 3, 4, 5]
引用類型,變量保存的就是一個內(nèi)存中的一個指針。所以,當(dāng)原型上面的屬性是一個引用類型的值時,我們通過其中某一個實例對原型屬性的更改,結(jié)果會反映在所有實例上面,這也是原型 共享
屬性造成的最大問題
另一個問題就是我們在創(chuàng)建子類型(比如上面的 p
)時,沒有辦法向超類型( Person
)的構(gòu)造函數(shù)中傳遞參數(shù)
關(guān)于JS關(guān)于高頻題 原型與原型鏈的面試問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。