溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript中的原型和繼承舉例分析

發(fā)布時(shí)間:2021-11-20 11:56:15 來(lái)源:億速云 閱讀:98 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“JavaScript中的原型和繼承舉例分析”,在日常操作中,相信很多人在JavaScript中的原型和繼承舉例分析問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”JavaScript中的原型和繼承舉例分析”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

介紹

JavaScript是一種基于原型的語(yǔ)言,這意味著對(duì)象屬性和方法可以通過(guò)具有克隆和擴(kuò)展能力的通用對(duì)象共享。這被稱為原型繼承,與類繼承不同。在流行的面向?qū)ο?span id="augoowm" class="wp_keywordlink">編程語(yǔ)言中,JavaScript是相對(duì)獨(dú)特的,因?yàn)槠渌恼Z(yǔ)言,如PHP、Python和Java都是基于類的語(yǔ)言,它們將類定義為對(duì)象的藍(lán)圖。

在文中,我們將學(xué)習(xí)什么是對(duì)象原型,以及如何使用構(gòu)造函數(shù)將原型擴(kuò)展為新對(duì)象。我們還將學(xué)習(xí)繼承和原型鏈。

JavaScript原型

JavaScript中的每個(gè)對(duì)象都有一個(gè)稱為[[Prototype]]的內(nèi)部屬性。我們可以通過(guò)創(chuàng)建一個(gè)新的空對(duì)象來(lái)演示這一點(diǎn)。

let x = {};

這是我們通常創(chuàng)建對(duì)象的方法,但是請(qǐng)注意,另一種實(shí)現(xiàn)方法是使用對(duì)象構(gòu)造函數(shù):

let x = new object()

包圍[[Prototype]]的雙方括號(hào)表示它是一個(gè)內(nèi)部屬性,不能在代碼中直接訪問(wèn)。

要找到這個(gè)新創(chuàng)建對(duì)象的[[Prototype]],我們將使用getPrototypeOf()方法。

Object.getPrototypeOf(x);

輸出將由幾個(gè)內(nèi)置屬性和方法組成。

輸出:

{constructor: ?, __defineGetter__: ?, __defineSetter__: ?, …}

找到的另一種方法[[Prototype]]是通過(guò)__proto__財(cái)產(chǎn)。__proto__是一個(gè)公開(kāi)[[Prototype]]對(duì)象內(nèi)部的屬性。

需要注意的是,. _proto__是一個(gè)遺留特性,不應(yīng)該在生產(chǎn)代碼中使用,而且它也不是在每個(gè)現(xiàn)代瀏覽器中都存在。但是,我們可以在本文中使用它來(lái)進(jìn)行演示。

x.__proto__;

輸出將與使用getPrototypeOf()相同。

輸出

{constructor: ?, __defineGetter__: ?, __defineSetter__: ?, …}

重要的是JavaScript中的每個(gè)對(duì)象都有一個(gè)[[Prototype]],因?yàn)樗鼮槿魏蝺蓚€(gè)或多個(gè)對(duì)象創(chuàng)建了鏈接的方法。

您創(chuàng)建的對(duì)象和內(nèi)置對(duì)象(如Date和Array)一樣具有[[Prototype]]。可以通過(guò)prototype屬性將這個(gè)內(nèi)部屬性從一個(gè)對(duì)象引用到另一個(gè)對(duì)象,我們將在本教程的后面看到這一點(diǎn)。

原型繼承

當(dāng)您試圖訪問(wèn)對(duì)象的屬性或方法時(shí),JavaScript將首先搜索對(duì)象本身,如果沒(méi)有找到,它將搜索對(duì)象的[[Prototype]]。如果在查詢對(duì)象及其[[Prototype]]后仍然沒(méi)有找到匹配項(xiàng),JavaScript將檢查被鏈接對(duì)象的原型,并繼續(xù)搜索,直到到達(dá)原型鏈的末端。

原型鏈的末尾是Object.prototype。所有對(duì)象都繼承對(duì)象的屬性和方法。任何超出鏈末端的搜索都會(huì)導(dǎo)致null。

在我們的示例中,x是一個(gè)從object繼承而來(lái)的空對(duì)象。x可以使用對(duì)象具有的任何屬性或方法,比如toString()。

x.toString();

輸出

[object Object]

這個(gè)原型鏈只有一個(gè)鏈長(zhǎng)。x - > Object。我們知道這一點(diǎn),因?yàn)槿绻覀冊(cè)噲D將兩個(gè)[[Prototype]]屬性鏈接在一起,它將為null。

x.__proto__.__proto__;

輸出

null

讓我們看看另一種類型的對(duì)象。如果您有使用JavaScript處理數(shù)組的經(jīng)驗(yàn),就會(huì)知道它們有許多內(nèi)置方法,比如pop()和push()。創(chuàng)建新數(shù)組時(shí)可以訪問(wèn)這些方法的原因是,創(chuàng)建的任何數(shù)組都可以訪問(wèn)array .prototype上的屬性和方法。

我們可以通過(guò)創(chuàng)建一個(gè)新的數(shù)組來(lái)測(cè)試它。

let y = [];

請(qǐng)記住,我們也可以把它寫(xiě)成數(shù)組構(gòu)造函數(shù),讓y = new array()。

如果我們查看新y數(shù)組的[[Prototype]],我們將看到它比x對(duì)象具有更多的屬性和方法。它繼承了Array.prototype中的所有內(nèi)容。

y.__proto__;
[constructor: ?, concat: ?, pop: ?, push: ?, …]

您將注意到原型上的構(gòu)造函數(shù)屬性被設(shè)置為Array()。構(gòu)造函數(shù)屬性返回對(duì)象的構(gòu)造函數(shù),這是一種用于從函數(shù)構(gòu)造對(duì)象的機(jī)制。

我們現(xiàn)在可以將兩個(gè)原型鏈接在一起,因?yàn)樵谶@種情況下,我們的原型鏈更長(zhǎng)。它看起來(lái)像y -> Array -> Object。

y.__proto__.__proto__;
{constructor: ?, __defineGetter__: ?, __defineSetter__: ?, …}

這個(gè)鏈現(xiàn)在引用Object.prototype。我們可以根據(jù)構(gòu)造函數(shù)的Prototype屬性測(cè)試內(nèi)部的[[Prototype]],以確定它們引用的是相同的東西。

y.__proto__ === Array.prototype;            // truey.__proto__.__proto__ === Object.prototype; // true

我們還可以使用isPrototypeOf()方法來(lái)實(shí)現(xiàn)這一點(diǎn)。

Array.prototype.isPrototypeOf(y);      // trueObject.prototype.isPrototypeOf(Array); // true

我們可以使用instanceof操作符來(lái)測(cè)試構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在對(duì)象原型鏈中的任何位置。

y instanceof Array; // true

總而言之,所有JavaScript對(duì)象都具有隱藏的內(nèi)部[[Prototype]]屬性(可能__proto__在某些瀏覽器中公開(kāi))。對(duì)象可以擴(kuò)展,并將繼承[[Prototype]]其構(gòu)造函數(shù)的屬性和方法。

這些原型可以被鏈接,并且每個(gè)額外的對(duì)象將繼承整個(gè)鏈中的所有內(nèi)容。鏈以O(shè)bject.prototype結(jié)束。

構(gòu)造器函數(shù)

構(gòu)造函數(shù)是用來(lái)構(gòu)造新對(duì)象的函數(shù)。new操作符用于基于構(gòu)造函數(shù)創(chuàng)建新實(shí)例。我們已經(jīng)看到了一些內(nèi)置的JavaScript構(gòu)造函數(shù),比如new Array()和new Date(),但是我們也可以創(chuàng)建自己的自定義模板來(lái)構(gòu)建新對(duì)象。

例如,我們正在創(chuàng)建一個(gè)非常簡(jiǎn)單的基于文本的角色扮演游戲。用戶可以選擇一個(gè)角色,然后選擇他們將擁有的角色類別,例如戰(zhàn)士、治療者、小偷等等。

由于每個(gè)字符將共享許多特征,例如具有名稱、級(jí)別和生命值,因此創(chuàng)建構(gòu)造函數(shù)作為模板是有意義的。然而,由于每個(gè)角色類可能有非常不同的能力,我們希望確保每個(gè)角色只能訪問(wèn)自己的能力。讓我們看看如何使用原型繼承和構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn)。

首先,構(gòu)造函數(shù)只是一個(gè)普通函數(shù)。當(dāng)使用new關(guān)鍵字的實(shí)例調(diào)用它時(shí),它將成為一個(gè)構(gòu)造函數(shù)。在JavaScript中,我們按照慣例將構(gòu)造函數(shù)的第一個(gè)字母大寫(xiě)。

// Initialize a constructor function for a new Herofunction Hero(name, level) {  this.name = name;  this.level = level;}

我們創(chuàng)建了一個(gè)名為Hero的構(gòu)造函數(shù),它有兩個(gè)參數(shù):name和level。因?yàn)槊總€(gè)字符都有一個(gè)名稱和一個(gè)級(jí)別,所以每個(gè)新字符都有這些屬性是有意義的。this關(guān)鍵字將引用創(chuàng)建的新實(shí)例,因此將this.name設(shè)置為name參數(shù)將確保新對(duì)象具有name屬性集。

現(xiàn)在我們可以用new創(chuàng)建一個(gè)新的實(shí)例。

let hero1 = new Hero('Bjorn', 1);

如果我們?cè)诳刂婆_(tái)輸出hero1,我們將看到已經(jīng)創(chuàng)建了一個(gè)新對(duì)象,其中新屬性按預(yù)期設(shè)置。

輸出

Hero {name: "Bjorn", level: 1}

現(xiàn)在,如果我們得到hero1的[[Prototype]],我們將能夠看到構(gòu)造函數(shù)Hero()。(記住,它的輸入與hero1相同。,但這是正確的方法。)

Object.getPrototypeOf(hero1);

輸出

constructor: ? Hero(name, level)

您可能注意到,我們只在構(gòu)造函數(shù)中定義了屬性,而沒(méi)有定義方法。在JavaScript中,為了提高效率和代碼可讀性,通常在原型上定義方法。

我們可以使用prototype向Hero添加一個(gè)方法。我們將創(chuàng)建一個(gè)greet()方法。

// Add greet method to the Hero prototypeHero.prototype.greet = function () {  return `${this.name} says hello.`;}

因?yàn)間reet()在Hero的原型中,而hero1是Hero的一個(gè)實(shí)例,所以這個(gè)方法對(duì)hero1是可用的。

hero1.greet();

輸出

"Bjorn says hello."

如果檢查Hero的[[Prototype]],您將看到greet()現(xiàn)在是一個(gè)可用選項(xiàng)。

這很好,但是現(xiàn)在我們想要為英雄創(chuàng)建角色類。將每個(gè)類的所有功能都放到Hero構(gòu)造函數(shù)中是沒(méi)有意義的,因?yàn)椴煌念惥哂胁煌墓δ堋N覀兿M麆?chuàng)建新的構(gòu)造函數(shù),但也希望它們連接到原始的Hero。

我們可以使用call()方法將屬性從一個(gè)構(gòu)造函數(shù)復(fù)制到另一個(gè)構(gòu)造函數(shù)。讓我們創(chuàng)建一個(gè)戰(zhàn)士和一個(gè)治療構(gòu)造器。

// Initialize Warrior constructorfunction Warrior(name, level, weapon) {  // Chain constructor with call  Hero.call(this, name, level);  // Add a new property  this.weapon = weapon;}// Initialize Healer constructorfunction Healer(name, level, spell) {  Hero.call(this, name, level);  this.spell = spell;}

兩個(gè)新的構(gòu)造函數(shù)現(xiàn)在都具有Hero和unqiue的屬性。我們將把a(bǔ)ttack()方法添加到Warrior中,而heal()方法添加到Healer中。

Warrior.prototype.attack = function () {  return `${this.name} attacks with the ${this.weapon}.`;}Healer.prototype.heal = function () {  return `${this.name} casts ${this.spell}.`;}

此時(shí),我們將使用兩個(gè)可用的新字符類創(chuàng)建字符。

const hero1 = new Warrior('Bjorn', 1, 'axe');const hero2 = new Healer('Kanin', 1, 'cure');

hero1現(xiàn)在被認(rèn)為是擁有新屬性的戰(zhàn)士。

輸出

Warrior {name: "Bjorn", level: 1, weapon: "axe"}

我們可以使用我們?cè)趹?zhàn)士原型上設(shè)置的新方法。

hero1.attack();
Console"Bjorn attacks with the axe."

但是如果我們嘗試使用原型鏈下面的方法會(huì)發(fā)生什么呢?

hero1.greet();

輸出

Uncaught TypeError: hero1.greet is not a function

使用call()鏈接構(gòu)造函數(shù)時(shí),原型屬性和方法不會(huì)自動(dòng)鏈接。我們將使用Object.create()來(lái)鏈接原型,確保在創(chuàng)建并添加到原型的任何其他方法之前將其放置。

Warrior.prototype = Object.create(Hero.prototype);Healer.prototype = Object.create(Hero.prototype);// All other prototype methods added below…

現(xiàn)在我們可以在一個(gè)戰(zhàn)士或治療者的實(shí)例上成功地使用Hero的原型方法。

hero1.greet();

輸出

"Bjorn says hello."

這里是我們的角色創(chuàng)建頁(yè)面的完整代碼。

// Initialize constructor functionsfunction Hero(name, level) {  this.name = name;  this.level = level;} function Warrior(name, level, weapon) {  Hero.call(this, name, level);   this.weapon = weapon;} function Healer(name, level, spell) {  Hero.call(this, name, level);   this.spell = spell;} // Link prototypes and add prototype methodsWarrior.prototype = Object.create(Hero.prototype);Healer.prototype = Object.create(Hero.prototype); Hero.prototype.greet = function () {  return `${this.name} says hello.`;} Warrior.prototype.attack = function () {  return `${this.name} attacks with the ${this.weapon}.`;} Healer.prototype.heal = function () {  return `${this.name} casts ${this.spell}.`;} // Initialize individual character instancesconst hero1 = new Warrior('Bjorn', 1, 'axe');const hero2 = new Healer('Kanin', 1, 'cure');

使用這段代碼,我們已經(jīng)用基本屬性創(chuàng)建了Hero類,從原始構(gòu)造函數(shù)創(chuàng)建了兩個(gè)名為Warrior和Healer的字符類,向原型添加了方法,并創(chuàng)建了單獨(dú)的字符實(shí)例。

到此,關(guān)于“JavaScript中的原型和繼承舉例分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI