精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
什么是原型鏈

對象原型

相信大家都這樣用過 map

let arr = [0, 1, 2]let doubleArr = arr.map(c => c * 2)console.log(doubleArr) // 0, 2, 4

不知道你有沒有想過, arr 本身并沒有設定 map 屬性,那為什么可以用 map 這個函數呢?

把它打印出來看看:

console.log(arr)// 0: 0// 1: 1// 2: 2// length: 3// __proto__: Array(0)

出現了一個名為 __proto__ 的對象,如果再將其展開,就會看到所有 Array 對象可以使用的函數;當然我們也可以在其中找到 map 函數,而這也正是例子中所調用的 arr.map 這個函數:

console.log(arr.map === arr.__proto__.map) // true

這里出現的 __proto__ 對象,也就是所謂的 原型對象(Prototype)

不同于 Java、C# 等基于類(Class) 的面向對象語言,通過定義類、創建實例、指定繼承等方式來傳遞屬性及方法;Javascript 則是個基于原型(Prototype)的對語言 ,通過預先建立出的原型對象,當新對象建立時指定對象的原型要參照哪個原型對象。

而當我們調用對象的屬性或方法時,若對象本身沒有這個屬性或方法,JavaScript 會自動尋找它原型中的方法,這也就是為什么可以直接調用 arr.map 而不會出錯的原因。

原型鏈

你可能已經發現在前面的例子中,__proto__ 對象里仍然有 __proto__ 屬性:

console.log(arr.__proto__) // Array 的 Prototypeconsole.log(arr.__proto__.__proto__) // Object 的 Prototypeconsole.log(arr.__proto__.__proto__.__proto__) // null

在上述機制中,每當對象建立時都會綁定原型,既然對象都有原型,對象原型本身也是對象,自然也不例外;由這個例子中我們可以看出:

  • arr 是數組實例,原型是 Array

  • arr.__proto__ 是數組的原型,原型是 Object

  • arr.__proto__.__proto__ 是對象的原型,原型是 null

  • arr.__proto__.__proto__.__proto__ 是 null,沒有任何屬性

由于每個對象都有原型,這樣就形成了一個關聯一個、層層相互依賴的從屬關系,我們把它們叫做原型鏈(Prototype Chain) ;通過這種機制,讓對象得以使用原型中的屬性和方法,并憑借原型鏈一層一層的按順序繼承,讓對象能擁有原型鏈上所有原型的功能,這就是 JavaScript 對象背后的運作機制。

補充:在 JavaScript 中,幾乎每個原型鏈的末端都會是 Object,并最后指向到 null

使用原型

說了這么多,該來點代碼了,接下來就來練習一下原型的建立、設定及修改吧。

先來創建一個新的對象構造函數:

function Person(name) { this.name = name}Person.prototype.hello = function () { console.log(`Hello ${this.name}.`)}let gary = new Person('Gary')gary.hello() // Hello Gary.Object.getPrototypeOf(gary) // {hello: ?, constructor: ?}

上面的例子創建了一個簡單的對象構造函數 Person(),并在構造函數中設定對象屬性。對象的方法中,由于方法不需要讓每個對象都獨自擁有一份,以避免造成冗余的內存消耗,應該要像前面 Array.prototype.map 的例子那樣把對象的方法設定給原型對象(Person.prototype),讓這個構造函數創建出來的對象都可以共用這些方法。最后建立一個新的 Person 對象,并通過 getPrototypeOf(obj) 獲取新產生對象的原型。

Q:為什么不直接用 __proto__ 獲取原型對象?
A:因為雖然 __proto__ 被幾乎所有的瀏覽器支持,但它是非標準屬性;通過 getPrototypeOf 取得對象的原型是正確的方法。

提醒:Person.prototype 不是 Person 的原型,而是構造函數執行后所建立的新對象的原型;千萬不要把構造函數的 prototype 屬性對象的原型搞混!

原型繼承

接著再創建新的對象原型,并繼承 Person

function Engineer(name, skill) {  Person.call(this, name)  this.skill = skill}Engineer.prototype = Object.create(Person.prototype)Engineer.prototype.constructor = Engineerlet alice = new Engineer('Alice', 'JavaScript')alice.hello() // Hello Alice.console.log(alice.skill) // JavaScriptObject.getPrototypeOf(alice) // Person {constructor: ?}

這里建立了新的對象 Engineer 的原型,并通過 Engineer.prototype 的指定,讓它的原型繼承自 Person.prototype,最后再重新設定 Engineer.prototype.constructor,讓構造函數重新指回自己;這樣就完成了最基本的原型繼承。

Q:為什么需要重新設定 constructor
A:這邊功過 Object.create 復制了 Person.prototype 的全部屬性,連同 constructor 屬性都會被覆蓋掉,如果 constructor 屬性錯誤,在做 instanceof 判斷時會產生錯誤的結果;因此這邊設定繼承時需要再次將 constructor 重新指定回構造函數本身。

修改原型

原型的引用、繼承是直接參照到原型對象上,并非是在每個對象都復制一份原型;因此可以利用這個特性,在原型上增加自定義的屬性和方法,讓所有該類型的對象都能得到新方法;許多針對舊版瀏覽器的 Polyfill 就是這樣實現的。

例如我們在寫 Vue 項目的時候,可能都有做過類似的操作,把共用性高的屬性方法放到 Vue.prototype 中:

Object.defineProperty(Vue.prototype, '$date', { value: dateTimeFormat })// 之后就可以這樣用vm.$date(dateObj)

這樣的確很方便,但也要提醒開大家,當我們在做原型修改的時候要特別小心。接著剛才的例子,如果嘗試對 Person 原型中的方法做個修改:

Person.prototype.hello = function () {  console.log(`Bye ${this.name}.`)}gary.hello() // Bye Gary.alice.hello() // Bye Alice.

如結果所示,當對象原型做修改時,所有原型鏈上有這個原型的對象,通通都會受到影響,不管對象是在修改前還是修改后創建的。

建議大家除非是 Polyfill,否則應該要極力避免對原生對象的原型進行修改,防止造成可能的意外結果。

ES6 的 Class

看完前面這一大段,是不是覺得心很累?別擔心,從 ES6 開始添加了 Class 語法糖,使開發者體驗提升了很多。下面把前面的例子用 Class 重構一下:

class Person { constructor (name){ this.name = name } // 方法會自動放到 Person.prototype hello() { console.log(`Hello ${this.name}.`) }}class Engineer extends Person { constructor (name, skill){ super(name) // 調用 Person 的構造函數 this.skill = skill }}let alice = new Engineer('Alice', 'JavaScript')alice.hello() // Hello Alice.Object.getPrototypeOf(alice) // Person {constructor: ?}

很方便,同樣的功能,代碼的可讀性卻提高了不少,繁瑣的設定也都能交給語法自動幫你完成。不過方便的語法背后,底層仍然是對象原型及原型鏈。

總結

以上是 JavaScript 中關于對象原型的說明,希望能幫你理解對象原型,在這個什么都是對象的語言中,充分理解并掌握對象原型,是成為專業碼農必須要突破的關卡之一。

本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
學習 JavaScript 最難點之一
JS學習筆記 原型鏈和利用原型實現繼承
徹底理解JavaScript原型
徹底弄懂 JS 原型與繼承
JavaScript前端開發案例教程-第12章 面向對象編程
JS原型與原型鏈終極詳解
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 寿阳县| 深水埗区| 梓潼县| 宁城县| 察雅县| 海原县| 汝阳县| 昂仁县| 如东县| 竹溪县| 大厂| 阜阳市| 罗平县| 浮山县| 华容县| 麟游县| 海兴县| 昆山市| 尤溪县| 巴南区| 子洲县| 麟游县| 元阳县| 吴堡县| 西昌市| 师宗县| 防城港市| 长武县| 会理县| 东莞市| 洪雅县| 武威市| 黔南| 抚顺市| 五常市| 长兴县| 广德县| 永安市| 玉屏| 同仁县| 宜宾市|