最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript 的原型鏈機制與繼承詳解

 更新時間:2026年05月23日 14:19:20   作者:HeyCeleste  
這段文章詳細解釋了JavaScript中的原型和原型鏈機制,以及它們如何實現(xiàn)屬性和方法的共享,文章還探討了通過原型鏈查找屬性的方法,并對比了多種實現(xiàn)繼承的方式,包括原型鏈繼承、構造函數繼承、組合機制和寄生組合機制,感興趣的朋友一起看看吧

JavaScript 的原型和原型鏈,本質是一種基于對象的繼承機制。通過原型鏈實現(xiàn)屬性查找和方法復用,使多個對象能夠共享同一份數據和行為,同時避免重復創(chuàng)建,提高性能。

原型

官方定義(MDN):

當使用 new 調用函數時,構造函數的 prototype 屬性將成為結果對象的原型。

原型是幾乎每個對象實例都有的內部屬性 [[Prototype]](可以通過定義在 Object.prototype 上的訪問器屬性 .__proto__訪問),在通過 new 創(chuàng)建對象時這個屬性就指向構造函數的 prototype 屬性。

訪問器屬性: 這是與普通屬性(數據屬性)不同的概念,數據屬性用于存儲值,而訪問器屬性通過 getter/setter 定義屬性的讀取和賦值行為。普通屬性例如 obj.name = "G.E.M." 就像是一個箱子將對應的值存儲起來,而訪問器屬性并不是一個儲物箱而是訪問器。

從底層來看,.__proto__ 其實是通過 getter/setter 這兩個函數觸發(fā)了引擎內部方法 [[GetPrototypeOf]][[SetPrototypeOf]]從而間接讀寫內部屬性 [[Prototype]]。ES5 標準更推薦 Object.getPrototypeOf()Object.setPrototypeOf() 這兩個方法讀寫對象實例的原型,.__proto__ 僅為保證瀏覽器向下兼容而存在。

這里值得注意的是,構造函數也有原型,即內部屬性 [[Prototype]],但這和它的 prototype 屬性并不是一個東西。構造函數本質上是對象,所以它自然也有原型,而 prototype 屬性是普通函數(非箭頭函數)在創(chuàng)建時自帶的、用來為未來實例當作公共池子的普通屬性,用于實現(xiàn)對象的繼承。

而原型對象上有個 constructor 屬性指向構造函數本身。也就是說,在默認情況下,如果假設 const obj = new Foo(),那么就存在 Object.getPrototypeof(obj).constructor === Foo。

綜上所述,每個對象身上都有一個內部屬性 [[Prototype]],指向一個對象,即原型對象。想要訪問到這個對象可以通過 Object.getPrototypeof() 或者 .__proto__,這兩個方法都會指向原型對象。并且,在實例被創(chuàng)建時,會將其原型指向構造函數的普通屬性 prototype,而在這個原型對象上又有一個 constructor 屬性指向構造函數本身。關系如下圖所示:

核心作用:

原型的本質作用就是實現(xiàn)屬性和方法的共享。原型機制通過“引用共享”,避免了函數的重復創(chuàng)建,提高了內存的利用率。

原型鏈

當訪問一個對象屬性時,JavaScript 引擎會先查對象本身,如果本身沒有這個屬性就會查它的原型,如果還沒有就繼續(xù)往上找直到找到 null,這條路徑就是原型鏈。

原型鏈上最后一個真實存在的對象是 Object.prototype,它的內部屬性 [[Prototype]] 指向 null,即原型鏈的終點,因為絕大多數對象都是從 Object.prototype 繼承而來的。

繼承

傳統(tǒng)面向對象語言(如 Java/C++)中繼承意味著復制,派生類會將基類的屬性和方法硬拷貝一份到自己身上,而 JavaScript 是利用原型鏈機制模擬了一種“假繼承”。在 new 一個對象的時候,JavaScript 并沒有像傳統(tǒng)類那樣復制藍圖上的共享方法,而是通過原型機制將對象實例的原型鏈接到構造函數的 prototype 上。

《You Don’t Know JS》中的觀點是 JS 底層并沒有類的概念,所謂的“繼承”是將兩個對象通過 [[Prototype]] 建立的一條單向引用鏈條(也就是原型鏈)。作者認為這種行為應該稱之為“行為委托”而不是繼承:當我們訪問一個對象 A 身上不存在的屬性/方法時,它并不是從其他地方復制或繼承了這個屬性/方法,而是順著原型鏈找到另一個對象 B,并將這個行為委托給 B 讓它幫執(zhí)行,此時 A 的執(zhí)行上下文(this)依舊綁定在它身上。

但由于普遍的用語習慣,下面還是將這一委托行為稱為繼承。

JS 實現(xiàn)繼承的幾種方式:

原型鏈繼承

function Base() {
    this.names = ['G.E.M.', 'Gloria'];
}
Base.prototype.say = function() {}
function Derived() {}
Derived.prototype = new Base();
const d1 = new Derived();
const d2 = new Derived();
d1.say(); // 能夠訪問到

這是早起最容易想到的辦法,直接將派生類的原型換成基類的實例。但這樣實現(xiàn)的一個痛點就是引用類型的數據會被所有派生類實例共享,例如此處 d1.names.push('Tang') 會導致 d2.names 也跟著改變。

構造函數繼承

function Base(name) {
    this.name = name;
    this.say = function() {console.log(this.name);}
}
function Derived(name, age) {
    Base.call(this, name); // 借用基類的構造函數并把 this 綁定到當前實例上
    this.age = age;
}

這樣實現(xiàn)繼承會使得每次實例化派生類都會把方法重新創(chuàng)建一遍,沒有實現(xiàn)函數引用的復用,造成內存嚴重浪費。

組合機制

這是將上述二者結合了起來:

function Base(name) {
    this.name = name;
    this.names = ['G.E.M.', 'Gloria'];
}
Base.prototype.say = function() {} // 將方法掛在原型上實現(xiàn)復用
function Derived(name, age) {
    Base.call(this, name); // 拿到獨立的屬性
    this.age = age;
}
Derived.prototype = new Base(); // 將原型鏈拉通
Derived.prototype.constructor = Derived; // 手動修復 constructor

組合機制的痛點是調用了兩次基類的構造函數,執(zhí)行 Base.call(this, name) 后派生類實例就創(chuàng)建了自己獨立的屬性,但 Derived.prototype = new Base()Derived.prototype 上也有了 Base 的那份屬性,但這上面的屬性并不會用到,造成內存浪費。

寄生組合機制

這是 ES5 時代最完美的手寫方案。

function Base(name) {
    this.name = name;
}
Base.prototype.say = function() {}
function Derived(name, age) {
    Base.call(this, name);
    this.age = age;
}
// 最優(yōu)方案是利用 Object.create() 直接建立干凈的原型鏈
// 這個方法實現(xiàn)了 Derived.prototype.[[Prototype]] = Base.prototype
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

ES6 的 class/extends

class 和 extends 是 ES6 官方推出的基于寄生組合機制繼承的語法糖。

class Base {
    constructor(name) {
        this.name = name;
    }
    say() {}
}
class Derived extends Base {
    constructor(name, age) {
        super(name); // 這句相當于 Base.call(this, name)
        this.age = age;
    }
}

ES5 寄生組合機制中存在靜態(tài)屬性的丟失,也就是沒有建立起基類和派生類兩個函數對象之間的直接連接,ES6 推出的 extends 補全了這一點,底層實現(xiàn)了 Derived.__proto__ = Base

到此這篇關于JavaScript 的原型鏈機制與繼承詳解的文章就介紹到這了,更多相關js原型鏈機制與繼承內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

伊吾县| 抚宁县| 碌曲县| 衡阳市| 云林县| 勐海县| 泾源县| 白朗县| 洪湖市| 安仁县| 原平市| 禄丰县| 保德县| 阳西县| 二连浩特市| 阿拉善盟| 南投市| 松原市| 精河县| 靖安县| 喀喇沁旗| 洪江市| 丹凤县| 凤阳县| 灵璧县| 石狮市| 沐川县| 石屏县| 蛟河市| 图们市| 山东省| 攀枝花市| 邯郸市| 伊川县| 敖汉旗| 昌平区| 南平市| 奉新县| 大宁县| 车致| 铁力市|