搜尋
首頁web前端js教程JavaScript物件導向之Prototypes與繼承_js物件導向

一、前言

本文翻譯自微軟的牛人Scott Allen Prototypes and Inheritance in JavaScript ,本文對到底什麼是Prototype和為什麼透過Prototype能實現繼承做了詳細的分析和闡述,是理解JS OO 的佳作之一。翻譯不好的地方望大家修改補充。

二、正文
JavaScript中的物件導向不同於其他語言,在學習前最好忘記你所熟知的物件導向的概念。 JS中的OO更強大、更值得討論(arguably)、更有彈性。

1.類別和物件
JS從傳統觀點來說是物件導向的語言。屬性、行為組合成一個物件。比如說,JS中的array就是由屬性和方法(如push、reverse、pop 等)組合成的物件。
複製程式碼 程式碼如下:

var myArray = [1, 2]; myArray.push(3);
myArray.reverse();
myArray.pop();
var length = myArray.length;

問題是:這些方法(如push)從哪裡來的?有些靜態語言(例如JAVA)用class來定義一個物件的結構。但是JS是沒有」class"(classless)的語言,沒有一個叫做「Array」的類別定義了這些方法給每個array去繼承。因為JS是動態的,我們可以在需要的時候隨意的往物件中加入方法。例如,下面的程式碼定義了一個對象,表示二維空間中的座標,裡面有一個add方法。

複製程式碼 程式碼如下:
var point = {
x : 10,
y : 5,
add: function(otherPoint)
{
this.x = otherPoint.x;
this.y = otherPoint.y;
}
};

我們想讓每個point物件都有一個add方法。我們也希望所有的poin物件共用一個add方法,而不必把add方法加到所有的point物件當中。這就需要讓prototype登場了。

2.關於Prototypes
JS中每個物件都有一個隱含的屬性(state)-對另一個物件的引用,稱為物件的prototype.我們上面建立的array和point當然也都含有各自prototype的引用。 prototype引用是隱含的,但是它是ECMAScript已實現的,允許我們使用物件的_proto_(在Chrome中)屬性來取得它。從概念上理解我們可以認為物件和prototype的關係就像下圖所表示的:

prototype1作為開發者,我們將用Object.getPrototypeOf 函數來取代_proto_屬性來查看物件的prototype引用。在寫這篇文章的時候,Object.getPrototypeOf這個函數已經在Chrome,firefox,還有IE9中提供了支援。在未來還會有更多的瀏覽器來支援這項特性,這已經是ECMAScript的標準之一。我們可以用以下程式碼來證明myArray和我們先前建立的point物件確實引用了兩個不同的prototype物件。



複製程式碼 程式碼如下:
Object.getPrototype .getPrototypeOf(myArray);



在文章接下來的部分,我也會使用到_proto_,主要是因為_proto_在圖示和句子裡比較直觀。但要記住這不是規範的,Object.getPrototypeOf才是用來取得物件prototype的推薦方法。

2.1是什麼使Prototypes如此特別?

我們已經知道了array的push方法來自myArray的prototype物件。圖2是Chrome中的一個截圖,我們呼叫Object.getPrototypeOf方法來取得myArray的prototype物件。

ff852808_img002(en-us,MSDN_10)

圖2

注意到myArray的prototype物件裡包含了很多方法,例如push、pop還有reverse這些我們在開頭程式碼裡使用過的。 prototype物件才是push方法的唯一擁有者,但這個方法是如何透過myArray呼叫的呢?
複製程式碼 程式碼如下:

myArray.push(3);

myArray.push(3);

想明白它是怎麼實現的,第一步是認清Protytype一點兒不特殊。 Prototype就是一些物件。我們可以為這些物件添加方法、屬性,像其他任何的JS物件一樣。但同時Prototype也是一個特殊的物件。

Prototype的特殊是因為下列規則:當我們通知JS我們想要在一個物件上呼叫push方法或是讀取某個屬性的時候,解釋器(runtime)首先去尋找這個物件本身的方法或屬性。如果解釋器沒有找到該方法(或屬性)就會沿著_proto_引用去尋找物件的prototype中的各個成員。當我們在myArray中呼叫push方法,JS沒有在myArray物件中找到push,但是在myArray的prototype物件中找到了push,即呼叫了該push方法(圖3)。 ff852808_img003(en-us,MSDN_10)



圖 3

我所描述的這種行為本質上就是物件本身繼承了 它的prototype中的所有方法和屬性。我們在JS中不需要用class來實現這種繼承關係。即,一個JS物件從它的prototype中繼承一個特性。

圖3也告訴我們每個array物件都能維護自己的狀態(state)和成員。如果我們需要myArray的length屬性,JS將從myArray找到length的值而不會去prototype中去找。我們能運用這個特性來「override"一個方法,即,將需覆蓋的方法(像push)放到myArray自己的物件中。這樣做就可以有效的將prototype中的push方法隱藏掉。

3.共享Prototype
Prototype在JS中真正神奇的地方是多個​​物件能引用同一個prototype物件。例如,我們建立兩個陣列:複製程式碼
程式碼如下:

var mymyray = [var mymy 1, 2];var yourArray = [4, 5, 6];

這兩個數組將共享一個相同的prototype對象,下面的程式碼將返回true 複製程式碼
程式碼如下:

Object.getPrototypeOf(myArray) === Object.getPrototypeOf(yourArtotypeOf(your);
如果我們在兩個陣列中呼叫push方法,JS將呼叫他們共同的prototype中的push。

ff852808_img004(en-us,MSDN_10)

Prototype物件在JS中給我們這種繼承的特性,它們也允許我們共享方法的實作。 Prototype也是鍊式的。換句話說,prototype是一個對象,那麼prototype對像也可以擁有一個指向別的prototype對象的引用。從圖2可以看到prototype的_proto_屬性是一個不為null的值也指向另外一個prototype.當JS開始尋找成員變數的時候,例如push方法,它將沿著這些prototype的引用檢查每一個物件直到找到這個物件或達到鏈的尾部。這種鏈的方式更增加了JS中繼承和共享的靈活性。

接下來你也許會問:我怎麼設定自訂物件的prototype引用?例如,我們之前建立過的物件point,我們怎麼加入一個add方法到prototype物件中,讓所有的point物件都能繼承它?在我們回答這個問題之前,我們先了解一下JS中的函數.

4.關於Funciton
函數在JS中同樣也是物件。函數在JS中有很多重要的特性,在這篇文章中我們不能一一列舉。但像把一個函數賦值給一個變數或是將一個函數當作另一個函數的參數在現今的JS程式設計中是很基礎的方式。

我們需要關注的是:因為函數是對象,所以它擁有方法、屬性和一個prototype對象的引用。讓我們一起討論下面程式碼的意思:
複製程式碼 程式碼如下:

// this will return true:
typeof (Array) === "function"
// and so will this:
Object.getPrototypeOf(Array) === Object.getPrototypeOf(function () { })
// and this, too:
Array.prototype != null

第一行程式碼證明Array在JS中是一個函數。待會兒我們將看到怎樣呼叫Array函數來建立一個新的array物件。

第二行程式碼證明Array物件和function物件引用相同的prototype,就像我們之前看到的所有的array物件共用一個prototype。

最後一行證明Array函數有一個prototype屬性。千萬不要將這個prototype屬性和_proto_屬性混淆了。它們的使用目的和指向的物件都不相同。
複製程式碼 程式碼如下:

// true
Array.prototype == == .getPrototypeOf(myArray)
// also true
Array.prototype == Object.getPrototypeOf(yourArray);

我們用新學的知識重畫之前的圖片:🎜 >

ff852808_img005(en-us,MSDN_10)

                                  其中一個方法是:


複製程式碼 程式碼如下:
// create a new, empty object
var o = {};
// inherit from the same prototype as an array object
o.__proto__ = Array.prototype;
// now we can invarray method can invarray method ....
o.push(3);


儘管上面的程式碼看起來不錯,但問題是不是每個JS的環境都支援物件的_proto_屬性。幸運的是,JS內建一個標準的機制用來建立新物件同時設定物件的_proto_屬性,這就是「new」操作符。
複製程式碼 程式碼如下:

var o = new Array();

var o = new Array();
o
o 「new」操作符在JS中有三個重要的任務:首先,它建立一個新的空物件。接著,它設定這個新物件的_proto_屬性指向呼叫函數的prototype屬性。最後,執行呼叫函數同時把「this」指標指向新的物件。如果我們把上面的兩行程式碼展開,將得到以下的程式碼:
複製程式碼


程式碼如下:


var o = {};
o.__proto__ = Array.prototype;
Array.call(o);
o.push(3);


函數的「call」方法允許你呼叫一個函數同時指定這個函數裡面的"this"指向傳入的新物件。當然,我們也想透過上面的方法來創造我們自己的物件來實現物件的繼承,而這種函數就是我們所熟知的──建構子。

5.建構子
建構子是一個有兩個獨特標識的普通JS函數物件:

1.首字母大寫(容易辨識)。

2.用new運算元連接來建構新物件。
Array就是一個建構子-Array函式用new連接、首字母大寫。 JS中的Array函數是內建的,但任何人都可以建立自己的建構子。事實上,我們終於到了該為point物件來建立一個建構函式的時候了。
複製程式碼


程式碼如下:


var Point = function (x, y) {
this.x = x;
this.y = y;
this.add = function (otherPoint) {
this.x = otherPoint.x;
this.y = otherPoint .y;
}
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
p1.add(p2);

ff852808_img006(en-us,MSDN_10)上面的程式碼中我們使用new運算元和Point函數來建構一個point物件。在記憶體中你可以把最終的結果想成圖6所表示的樣子。





圖 6
現在的問題是,add方法存在於每一個point物件中。鑑於我們對prototype的了解,把add方法加到Point.prototype中是一個更好的選擇(不必把add方法的程式碼拷貝到每個物件中)。為了實現這個目的,我們需要在Point.prototype物件上做些修改。
複製程式碼


程式碼如下:


var Point = function (x, y) {
this.x = x;
this.y = y;
}
Point.prototype.add = function (otherPoint) {
this.x = otherPoint.x;
this.y = otherPoint.y;
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
p1.add(p2) ;

ff852808_img007(en-us,MSDN_10)好了!我們已經用prototype實作了JS中的繼承!


6.總結 希望能透過這篇文章讓你能撥開prototype的迷霧。當然這只是功能強大又靈活的prototype的入門。更多的關於prototype的知識還是希望讀者能夠自己去探索和發現。
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
解决PHP报错:继承父类时遇到的问题解决PHP报错:继承父类时遇到的问题Aug 17, 2023 pm 01:33 PM

解决PHP报错:继承父类时遇到的问题在PHP中,继承是一种重要的面向对象编程的特性。通过继承,我们能够重用已有的代码,并且能够在不修改原有代码的情况下,对其进行扩展和改进。尽管继承在开发中应用广泛,但有时候在继承父类时可能会遇到一些报错问题,本文将围绕解决继承父类时遇到的常见问题进行讨论,并提供相应的代码示例。问题一:未找到父类在继承父类的过程中,如果系统无

使用继承的Java程序来计算定期存款(FDs)和定期存款(RDs)的利息使用继承的Java程序来计算定期存款(FDs)和定期存款(RDs)的利息Aug 20, 2023 pm 10:49 PM

继承是一个概念,它允许我们从一个类访问另一个类的属性和行为。被继承方法和成员变量的类被称为超类或父类,而继承这些方法和成员变量的类被称为子类或子类。在Java中,我们使用“extends”关键字来继承一个类。在本文中,我们将讨论使用继承来计算定期存款和定期存款的利息的Java程序。首先,在您的本地机器IDE中创建这四个Java文件-Acnt.java−这个文件将包含一个抽象类‘Acnt’,用于存储账户详情,如利率和金额。它还将具有一个带有参数‘amnt’的抽象方法‘calcIntrst’,用于计

如何在PHP中使用多态和继承来处理数据类型如何在PHP中使用多态和继承来处理数据类型Jul 15, 2023 pm 07:41 PM

如何在PHP中使用多态和继承来处理数据类型引言:在PHP中,多态和继承是两个重要的面向对象编程(OOP)概念。通过使用多态和继承,我们可以更加灵活地处理不同的数据类型。本文将介绍如何在PHP中使用多态和继承来处理数据类型,并通过代码示例展示它们的实际应用。一、继承的基本概念继承是面向对象编程中的一种重要概念,它允许我们创建一个类,该类可以继承父类的属性和方法

PHP中的封装技术及应用PHP中的封装技术及应用Oct 12, 2023 pm 01:43 PM

PHP中的封装技术及应用封装是面向对象编程中的一个重要概念,它指的是将数据和对数据的操作封装在一起,以便提供对外部程序的统一访问接口。在PHP中,封装可以通过访问控制修饰符和类的定义来实现。本文将介绍PHP中的封装技术及其应用场景,并提供一些具体的代码示例。一、封装的访问控制修饰符在PHP中,封装主要通过访问控制修饰符来实现。PHP提供了三个访问控制修饰符,

如何使用Java强制继承代理final类?如何使用Java强制继承代理final类?Sep 06, 2023 pm 01:27 PM

如何使用Java强制继承代理final类?在Java中,final关键字用于修饰类、方法和变量,表示它们不可被继承、重写和修改。然而,在某些情况下,我们可能需要强制继承一个final类,以实现特定的需求。本文将讨论如何使用代理模式来实现这样的功能。代理模式是一种结构型设计模式,它允许我们创建一个中间对象(代理对象),该对象可以控制对另一个对象(被代理对象)的

PHP中的多重继承PHP中的多重继承Aug 23, 2023 pm 05:53 PM

继承:继承是面向对象编程(OOP)中的一个基本概念,它允许类从其他类继承属性和行为。它是一种基于现有类创建新类的机制,促进代码重用并建立类之间的层次关系。继承基于"父子"或"超类-子类"关系的概念。从中继承的类被称为超类或基类,而继承超类的类被称为子类或派生类。子类继承其超类的所有属性(变量)和方法(函数),还可以添加自己独特的属性和方法或覆盖继承的属性和方法继承的类型在面向对象编程(OOP)中,继承是一个基本概念,它允许类从其他类中继承属性和行为。它促进

如何在Go语言中实现封装和继承如何在Go语言中实现封装和继承Jul 23, 2023 pm 08:17 PM

如何在Go语言中实现封装和继承封装和继承是面向对象编程中的两个重要概念,它们可以使代码更加模块化和可维护,同时也为代码的复用提供了便利。本文将介绍在Go语言中如何实现封装和继承,并提供相应的代码示例。封装封装是将数据和功能进行封装,隐藏实现的细节,只暴露必要的接口给外部使用。在Go语言中,封装是通过导出和非导出标识符来实现的。首字母大写的标识符可以被其他包访

继承、多态与接口:PHP面向对象的三大特性继承、多态与接口:PHP面向对象的三大特性May 11, 2023 pm 03:45 PM

PHP是一种服务器端编程语言,自PHP5之后开始支持面向对象编程(OOP)。OOP的核心思想是将数据和行为封装在对象中,以提高程序的可维护性和可扩展性。在PHP中,面向对象编程具有三大特性:继承、多态与接口。一、继承继承是指一个类可以从另一个类中继承属性和方法。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以通过继承获得父类中的属性和方法,并且可

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。