摘要:前言作為前端高頻面試題之一,相信很多小伙伴都有遇到過(guò)這個(gè)問(wèn)題。
前言
作為前端高頻面試題之一,相信很多小伙伴都有遇到過(guò)這個(gè)問(wèn)題。那么你是否清楚完整的了解它呢?
國(guó)際慣例,讓我們先拋出問(wèn)題:
什么是原型、原型鏈
它們有什么特點(diǎn)
它們能做什么
怎么確定它們的關(guān)系
或許你已經(jīng)有答案,或許你開(kāi)始有點(diǎn)疑惑,無(wú)論是 get 新技能或是簡(jiǎn)單的溫習(xí)一次,讓我們一起去探究一番吧
如果文章中有出現(xiàn)紕漏、錯(cuò)誤之處,還請(qǐng)看到的小伙伴多多指教,先行謝過(guò)
以下↓
原型JavaScript 是基于原型的我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè) prototype(原型) 屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。
簡(jiǎn)單來(lái)說(shuō),就是當(dāng)我們創(chuàng)建一個(gè)函數(shù)的時(shí)候,系統(tǒng)就會(huì)自動(dòng)分配一個(gè) prototype屬性,可以用來(lái)存儲(chǔ)可以讓所有實(shí)例共享的屬性和方法
用一張圖來(lái)表示就更加清晰了:
圖解:
每一個(gè)構(gòu)造函數(shù)都擁有一個(gè) prototype 屬性,這個(gè)屬性指向一個(gè)對(duì)象,也就是原型對(duì)象
原型對(duì)象默認(rèn)擁有一個(gè) constructor 屬性,指向指向它的那個(gè)構(gòu)造函數(shù)
每個(gè)對(duì)象都擁有一個(gè)隱藏的屬性 __proto__,指向它的原型對(duì)象
function Person(){} var p = new Person(); p.__proto__ === Person.prototype // true Person.prototype.constructor === Person // true
那么,原型對(duì)象都有哪些特點(diǎn)呢
原型特點(diǎn)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
從這段代碼我們不難看出:
實(shí)例可以共享原型上面的屬性和方法
實(shí)例自身的屬性會(huì)屏蔽原型上面的同名屬性,實(shí)例上面沒(méi)有的屬性會(huì)去原型上面找
既然原型也是對(duì)象,那我們可不可以重寫(xiě)這個(gè)對(duì)象呢?答案是肯定的
function Person() {} Person.prototype = { name: "tt", age: 18, sayHi() { console.log("Hi"); } } var p = new Person()
只是當(dāng)我們?cè)谥貙?xiě)原型鏈的時(shí)候需要注意以下的問(wèn)題:
function Person(){} var p = new Person(); Person.prototype = { name: "tt", age: 18 } Person.prototype.constructor === Person // false p.name // undefined
一圖勝過(guò)千言萬(wàn)語(yǔ)
在已經(jīng)創(chuàng)建了實(shí)例的情況下重寫(xiě)原型,會(huì)切斷現(xiàn)有實(shí)例與新原型之間的聯(lián)系
重寫(xiě)原型對(duì)象,會(huì)導(dǎo)致原型對(duì)象的 constructor 屬性指向 Object ,導(dǎo)致原型鏈關(guān)系混亂,所以我們應(yīng)該在重寫(xiě)原型對(duì)象的時(shí)候指定 constructor( instanceof 仍然會(huì)返回正確的值)
Person.prototype = { constructor: Person }
注意:以這種方式重設(shè) constructor 屬性會(huì)導(dǎo)致它的 Enumerable 特性被設(shè)置成 true(默認(rèn)為false)
既然現(xiàn)在我們知道了什么是 prototype(原型)以及它的特點(diǎn),那么原型鏈又是什么呢?
原型鏈JavaScript 中所有的對(duì)象都是由它的原型對(duì)象繼承而來(lái)。而原型對(duì)象自身也是一個(gè)對(duì)象,它也有自己的原型對(duì)象,這樣層層上溯,就形成了一個(gè)類似鏈表的結(jié)構(gòu),這就是原型鏈
同樣的,我們使用一張圖來(lái)描述
所有原型鏈的終點(diǎn)都是 Object 函數(shù)的 prototype 屬性
Objec.prototype 指向的原型對(duì)象同樣擁有原型,不過(guò)它的原型是 null ,而 null 則沒(méi)有原型
清楚了原型鏈的概念,我們就能更清楚地知道屬性的查找規(guī)則,比如前面的 p 實(shí)例屬性.如果自身和原型鏈上都不存在這個(gè)屬性,那么屬性最終的值就是 undefined ,如果是方法就會(huì)拋出錯(cuò)誤
class類ES6 提供了 Class(類) 這個(gè)概念,作為對(duì)象的模板,通過(guò) class 關(guān)鍵字,可以定義類
為什么會(huì)提到 class :
ES6 的 class 可以看作只是一個(gè)語(yǔ)法糖,它的絕大部分功能,ES5 都可以做到,新的 class 寫(xiě)法只是讓對(duì)象原型的寫(xiě)法更加清晰、更像面向?qū)ο缶幊痰恼Z(yǔ)法而已
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } // 可以這么改寫(xiě) function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; };
class 里面定義的方法,其實(shí)都是定義在構(gòu)造函數(shù)的原型上面實(shí)現(xiàn)實(shí)例共享,屬性定義在構(gòu)造函數(shù)中,所以 ES6 中的類完全可以看作構(gòu)造函數(shù)的另一種寫(xiě)法
除去 class 類中的一些行為可能與 ES5 存在一些不同,本質(zhì)上都是通過(guò)原型、原型鏈去定義方法、實(shí)現(xiàn)共享。所以,還是文章開(kāi)始那句話 JavaScript是基于原型的
更多 class 問(wèn)題,參考這里
關(guān)系判斷instanceof
最常用的確定原型指向關(guān)系的關(guān)鍵字,檢測(cè)的是原型,但是只能用來(lái)判斷兩個(gè)對(duì)象是否屬于實(shí)例關(guān)系, 而不能判斷一個(gè)對(duì)象實(shí)例具體屬于哪種類型
function Person(){} var p = new Person(); p instanceof Person // true p instanceof Object // true
hasOwnProperty
通過(guò)使用 hasOwnProperty 可以確定訪問(wèn)的屬性是來(lái)自于實(shí)例還是原型對(duì)象
function Person() {} Person.prototype = { name: "tt" } var p = new Person(); p.age = 15; p.hasOwnProperty("age") // true p.hasOwnProperty("name") // false原型鏈的問(wèn)題
由于原型鏈的存在,我們可以讓很多實(shí)例去共享原型上面的方法和屬性,方便了我們的很多操作。但是原型鏈并非是十分完美的
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]
引用類型,變量保存的就是一個(gè)內(nèi)存中的一個(gè)指針。所以,當(dāng)原型上面的屬性是一個(gè)引用類型的值時(shí),我們通過(guò)其中某一個(gè)實(shí)例對(duì)原型屬性的更改,結(jié)果會(huì)反映在所有實(shí)例上面,這也是原型 共享 屬性造成的最大問(wèn)題
另一個(gè)問(wèn)題就是我們?cè)趧?chuàng)建子類型(比如上面的 p)時(shí),沒(méi)有辦法向超類型( Person )的構(gòu)造函數(shù)中傳遞參數(shù)
后記鑒于原型的特點(diǎn)和存在的問(wèn)題,所以我們?cè)趯?shí)際開(kāi)發(fā)中一般不會(huì)多帶帶使用原型鏈。一般會(huì)使用構(gòu)造函數(shù)和原型相配合的模式,當(dāng)然這也就牽扯出了 JavaScript 中另一個(gè)有意思的領(lǐng)域:繼承
那么,什么又是繼承呢
且聽(tīng)下回分解
最后,推薦一波前端學(xué)習(xí)歷程,不定期分享一些前端問(wèn)題和有意思的東西歡迎 star 關(guān)注 傳送門(mén)
參考文檔JavaScript高級(jí)程序設(shè)計(jì)
ECMAScript6入門(mén)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/103610.html
摘要:每一個(gè)由構(gòu)造函數(shù)創(chuàng)建的對(duì)象都會(huì)默認(rèn)的連接到該神秘對(duì)象上。在構(gòu)造方法中也具有類似的功能,因此也稱其為類實(shí)例與對(duì)象實(shí)例一般是指某一個(gè)構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象,我們稱為構(gòu)造函數(shù)的實(shí)例實(shí)例就是對(duì)象。表示該原型是與什么構(gòu)造函數(shù)聯(lián)系起來(lái)的。 本文您將看到以下內(nèi)容: 傳統(tǒng)構(gòu)造函數(shù)的問(wèn)題 一些相關(guān)概念 認(rèn)識(shí)原型 構(gòu)造、原型、實(shí)例三角結(jié)構(gòu)圖 對(duì)象的原型鏈 函數(shù)的構(gòu)造函數(shù)Function 一句話說(shuō)明什么...
摘要:不理解沒(méi)關(guān)系,下面會(huì)結(jié)合圖例分析上一篇高級(jí)程序設(shè)計(jì)筆記創(chuàng)建對(duì)象下一篇高級(jí)程序設(shè)計(jì)筆記繼承參考之原型鏈的解讀三張圖搞懂的原型對(duì)象與原型鏈繼承與原型鏈 文章直接從原型圖解開(kāi)始的,如果對(duì)一些概念不太清除,可以結(jié)合后面幾節(jié)查看 1. 圖解原型鏈 1.1 鐵三角關(guān)系(重點(diǎn)) function Person() {}; var p = new Person(); showImg(https://s...
摘要:綜上所述有原型鏈繼承,構(gòu)造函數(shù)繼承經(jīng)典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優(yōu)點(diǎn)于一身是實(shí)現(xiàn)基于類型繼承的最有效方法。 一、前言 繼承是面向?qū)ο螅∣OP)語(yǔ)言中的一個(gè)最為人津津樂(lè)道的概念。許多面對(duì)對(duì)象(OOP)語(yǔ)言都支持兩種繼承方式::接口繼承 和 實(shí)現(xiàn)繼承 。 接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于js中方法沒(méi)有簽名...
摘要:原型對(duì)象是由創(chuàng)建的,因此原型對(duì)象的構(gòu)造函數(shù)是構(gòu)造函數(shù)也可以是稱為對(duì)象,原型對(duì)象也就繼承了其生父構(gòu)造函數(shù)中的數(shù)據(jù),也同時(shí)繼承了原型對(duì)象的數(shù)據(jù)。當(dāng)然這條原型鏈中的數(shù)據(jù),會(huì)被還是還是這類構(gòu)造函數(shù)繼承,但是不會(huì)被這些繼承,他們不處于同一個(gè)鏈條上。 js中,F(xiàn)unction的本質(zhì)是什么?Object的本質(zhì)又是什么?js中有幾條原型鏈? showImg(https://segmentfault.c...
摘要:在節(jié)中,我們學(xué)習(xí)到了通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象的三個(gè)重要步驟,其中的一步是把構(gòu)造函數(shù)的對(duì)象設(shè)置為創(chuàng)建對(duì)象的原型。利用而不是直接用創(chuàng)建一個(gè)實(shí)例對(duì)象的目的是,減少一次調(diào)用父構(gòu)造函數(shù)的執(zhí)行。 JavaScript語(yǔ)言不像面向?qū)ο蟮木幊陶Z(yǔ)言中有類的概念,所以也就沒(méi)有類之間直接的繼承,JavaScript中只有對(duì)象,使用函數(shù)模擬類,基于對(duì)象之間的原型鏈來(lái)實(shí)現(xiàn)繼承關(guān)系,ES6的語(yǔ)法中新增了class關(guān)鍵...
閱讀 2414·2021-11-24 09:39
閱讀 3241·2021-10-09 09:53
閱讀 1135·2021-09-22 16:06
閱讀 4453·2021-09-02 10:18
閱讀 803·2021-08-23 09:42
閱讀 1766·2021-08-17 10:11
閱讀 2689·2019-08-30 13:02
閱讀 2125·2019-08-30 12:49