In diesem Artikel besprechen wir Prototypen und wie man sie für die Vererbung in JS verwendet. Wir werden auch sehen, wie sich Prototypmethoden von der klassenbasierten Vererbung unterscheiden.
Vererbung ist ein charakteristisches Merkmal von Programmiersprachen, das mit der Einführung objektorientierter Programmiersprachen entstand. Die meisten dieser Sprachen sind klassenbasierte Sprachen. Hier ist die Klasse wie eine Blaupause und das Objekt ist ihre Darstellung. Das heißt, um ein Objekt zu erstellen, müssen wir zuerst eine Klasse erstellen und dann können wir eine beliebige Anzahl von Objekten aus einer Klasse erstellen.
Stellen Sie sich vor, wir haben eine Klasse, die ein Smartphone darstellt. Diese Klasse verfügt wie andere Smartphones über Funktionen wie das Aufnehmen von Bildern und die GPS-Positionierung. Das Folgende ist eine Beschreibung einer solchen Klasse mit C++:
class SmartPhone { public: void captureImages() {} } SmartPhone x; x.captureImages()
Wir haben eine Klasse namens SmartPhone
erstellt, die über eine Methode namens capturePictures
zum Aufnehmen von Bildern verfügt. SmartPhone
的类,它有一个名为capturePictures
的方法用来拍照。
如果我们需要一个iPhone
类,它可以捕捉图像和一些特殊的功能,比如面部ID扫描。下面是两种可能的解决方案:
将捕获图像功能与其他常见的智能手机功能,以及iPhone的特定功能一起重写到一个新类中。但是这种方法需要更多的时间和精力,并且会引入更多的bug。
SmartPhone
类中的功能,这就是继承的作用,继承也是重用其他类/对象中功能的一种方式。这里是我们如何从SmartPhone
类中继承capturePictures
方法,使用 c++ 实现如下:
class Iphone: public SmartPhone { public: void faceIDScan() {} } Iphone x x.faceIDScan() x.captureImages()
上面是一个简单的继承示例。 但是,它表明继承可以使我们以某种方式重用代码,从而使所生成的程序更不易出错,并且花费更少的时间进行开发。
以下是关于类的一些重要信息:
值得注意的是,类本身并没有做任何事情。在从类创建对象之前,实际上没有完成任何工作。我们将看到它为什么不同于JavaScript。
在 JS 中,所有对象都有一个特殊的内部属性,该属性基本上是对另一个对象的引用。 此引用取决于对象的创建方式。 在 ECMAScript/JavaScript规范中,它表示为[[Prototype]]
。
由于[[Prototype]]
链接到一个对象,所以该对象有自己的[[Prototype]]
引用。这就是建立原型链的方式。
这个[[Prototype]]
链是 JS 中继承的构建块。
__proto__
对象为了访问对象的[[Prototype]]
,大多数浏览器都提供__proto__
属性。访问方式如下:
obj.__proto__
需要注意的是,这个属性不是 ECMAScript 标准的一部分,它实际上是由浏览器实现的。
除了__proto__
属性外,还有一种访问[[Prototype]]
的标准方法:
Object.getPrototypeOf(obj);
对应的有个类似的方法来设置对象的[[Prototype]]
:
Object.setPrototypeOf(obj, prototype);
[[Prototype]]
和.prototype
属性[[Prototype]]
只不过是一种用来表示物体原型的标准符号。 许多开发人员将其与.prototype
属性混淆,这是完全不同的事情,接着我们来研究一下.prototype
属性。
在 JS 中,有许多创建对象的方法。一种方法是使用构造函数,像这样使用new
关键字来调用它:
function SmartPhone(os) { this.os = os } let phone = new SmartPhone('Android')
在控制台打印 phone
对象:
{ os: "IPhone" __proto__{ constructor: ƒ SmartPhone(os) __proto__: Object } }
现在,如果我们希望在phone
对象上有一些方法,我们可以在函数上使用.prototype
属性,如下所示:
SmartPhone.prototype.isAndroid = function () { return this.os === 'Android' || 'android' }
再次创建phone
对象时,打印 phone
对象如下:
{ os: "Android" __proto__{ isAndroid: ƒ() constructor: ƒ SmartPhone(os) __proto__: Object } }
我们可以在对象的[[Prototype]]
中看到isAndroid()
方法。
简而言之,.prototype
属性基本上就像由给定的构造函数创建的[[Prototype]]
对象的蓝图。 在.prototype
属性/对象中声明的所有内容都会在对象的[[Prototype]]
中弹出。
实上,如果将 SmartPhone.prototype 与
phone 的[[Prototype]]
iPhone
-Klasse benötigen, die Bilder und einige spezielle Funktionen wie das Scannen von Gesichts-IDs erfassen kann. Hier sind zwei mögliche Lösungen:
SmartPhone
wiederverwenden. Dies ist die Rolle der Vererbung, die auch eine Möglichkeit darstellt, Funktionen in anderen Klassen/Objekten wiederzuverwenden. capturePictures
von der Klasse SmartPhone
, indem wir C++ wie folgt verwenden: 🎜console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype); // true
[[Prototype]]
dargestellt. 🎜🎜Da [[Prototype]]
mit einem Objekt verknüpft ist, verfügt das Objekt über eine eigene [[Prototype]]
-Referenz. So bauen Sie eine Prototypenkette auf. 🎜🎜Diese [[Prototype]]
-Kette ist der Baustein der Vererbung in JS. 🎜🎜__proto__
Objekt 🎜🎜Um auf den [[Prototype]]
des Objekts zuzugreifen, stellen die meisten Browser das Attribut __proto__
bereit. Der Zugriff erfolgt wie folgt: 🎜function ObjectA() { this.methodA = function () {} } let firstObj = new ObjectA() console.log(firstObj)
__proto__
gibt es auch eine Standardmethode für den Zugriff auf [[Prototype]]
: 🎜function MyObject() {} MyObject.prototype.propA = 10; // 在原型上创建属性 let myObject = new MyObject(); console.log(myObject.propA); // [[Prototype]]上的属性 // 10 myObject.propA = 20; // 对象的属性 console.log(myObject.propA); // 20
[[Prototype]]
-Eigenschaften des Objekts: 🎜let obj = {}
[[Prototype]]
und .prototype
-Eigenschaften 🎜🎜[ [Prototyp]]
ist nichts anderes als ein Standardsymbol, das zur Darstellung des Prototyps eines Objekts verwendet wird. Viele Entwickler verwechseln es mit dem Attribut .prototype
, was etwas völlig anderes ist. Werfen wir einen Blick auf das Attribut .prototype
. 🎜🎜In JS gibt es viele Möglichkeiten, Objekte zu erstellen. Eine Möglichkeit besteht darin, den Konstruktor zu verwenden und ihn mit dem Schlüsselwort new
wie folgt aufzurufen: 🎜let obj = new Object();
phone
-Objekt in der Konsole aus: 🎜let SmartPhone = { captureImages: function() {} } let Iphone = Object.create(SmartPhone) Iphone.captureImages()
phone
und wir können das Attribut .prototype
wie folgt für die Funktion verwenden: 🎜function SmartPhone(os) { this.os = os; } SmartPhone.prototype.isAndroid = function() { return this.os === 'Android'; }; SmartPhone.prototype.isIOS = function() { return this.os === 'iOS'; };
phone
erneut Beim Drucken des phone
-Objekts wie folgt: 🎜function Iphone() { SmartPhone.call(this, 'iOS'); }
isAndroid()
-Methode im [[Prototype]]
des Objekts sehen. 🎜🎜Kurz gesagt ist die Eigenschaft .prototype
im Grunde wie eine Blaupause für ein [[Prototype]]
-Objekt, das von einem bestimmten Konstruktor erstellt wurde. Alles, was in einer .prototype
-Eigenschaft/einem .prototype
-Objekt deklariert ist, wird im [[Prototype]]
des Objekts angezeigt. 🎜🎜Wenn Sie SmartPhone.prototype mit <code>[[Prototype]]
von phone vergleichen, werden Sie tatsächlich feststellen, dass sie gleich sind: 🎜Iphone.prototype = Object.create(SmartPhone.prototype);
function ObjectA() { this.methodA = function () {} } let firstObj = new ObjectA() console.log(firstObj)
这种方法的问题是当我们初始化一个新对象时。所有实例都有自己methodA
的副本。相反,当我们在函数的原型上创建它时,对象的所有实例只共享方法的一个副本,显然使用原型的方式效率会过高。
当我们访问一个属性以获取它时,会发生以下情况:
JS 引擎查找对象上的属性,如果找到了该属性,然后返回它。否则,JS 引擎将通过查看[[Prototype]]
来检查对象的继承属性,如果找到该属性,则返回它,否则,它会查找 [[Prototype]]
的[[Prototype]]
。 找到属性或没有[[Prototype]]
时,该链结束,这意味着我们已经到达原型链的末端。
当我们设置/创建属性时,JS 总是在对象本身上进行设置。 即使[[Prototype]]
链上存在相同的属性,下面是一个例子:
function MyObject() {} MyObject.prototype.propA = 10; // 在原型上创建属性 let myObject = new MyObject(); console.log(myObject.propA); // [[Prototype]]上的属性 // 10 myObject.propA = 20; // 对象的属性 console.log(myObject.propA); // 20
在上面的示例中,我们创建了一个构造函数,该函数的[[Prototype]]
上具有属性propA
。 当我们尝试对其进行读取操作时,会在控制台中看到该值。 但是,当我们尝试在对象本身上设置相同的属性时;JS 使用给定值在对象上创建一个新属性。 现在,如果我们不能直接访问[[Prototype]]
上的属性。
值得注意的是,普通对象的[[Prototype]]
链的末尾是内置的Object.prototype
。 这就是为什么大多数对象共享许多方法(例如toString()
)的原因。 因为它们实际上是在Object.prototype
上定义的。
在 JS 中,无论我们如何创建对象,只有原型继承,但这些方式还有一些区别,来看看:
在JavaScript中创建对象的最简单方法是使用对象字面量:
let obj = {}
如果在浏览器的控制台中打印obj
,我们将看到以下内容:
基本上,所有用文字面量创建的对象都继承了Object.prototype
的属性。
需要注意的是__proto__
对象引用了创建它的构造函数。 在这种情况下,constructor
属性指向Object
构造函数。
另一种不太常见的创建对象的方法是使用对象构造函数。JS 提供了一个名为Object
的内置构造函数方法来创建对象。
let obj = new Object();
这种方法的结果与对象字面量的方式相同。它从Object.prototype
继承属性。因为我们使用Object
作为构造函数。
使用此辅助方法,我们可以创建一个带有[[Prototype]]
的对象,如下所示:
let SmartPhone = { captureImages: function() {} } let Iphone = Object.create(SmartPhone) Iphone.captureImages()
这是在 JS 中使用继承的最简单方法之一。猜猜我们如何在没有任何[[Prototype]]
引用的情况下创建对象?
与 JS 运行时提供的对象构造函数相似。 我们还可以创建自己的构造函数,以创建适合我们需求的对象,如下所示:
function SmartPhone(os) { this.os = os; } SmartPhone.prototype.isAndroid = function() { return this.os === 'Android'; }; SmartPhone.prototype.isIOS = function() { return this.os === 'iOS'; };
现在,我们想创建一个iPhone
类,它应该有'iOS'
作为它 os
属性的值。它还应该有faceIDScan
方法。
首先,我们必须创建一个Iphone
构造函数,在其中,我们应该调用SmartPhone
构造函数,如下所示:
function Iphone() { SmartPhone.call(this, 'iOS'); }
这会将Iphone
构造函数中的this.os
属性设置为’iOS‘
。
之所以调用SmartPhone.call
方法,是因为我们需要更改 this
值以引用Iphone
。 这类似于在面向对象的世界中调用父级的构造函数。
接下来的事情是,我们必须从SmartPhone
构造函数继承方法。 我们可以在此处使用Object.create
朋友,如下所示:
Iphone.prototype = Object.create(SmartPhone.prototype);
现在,我们可以使用.prototype
为Iphone
添加方法,如下所示:
Iphone.prototype.faceIDScan = function() {};
最后,我们可以使用Iphone
创建一个对象,如下所示:
let x = new Iphone(); // calling inherited method console.log(x.isIOS()): // true
使用ES6,整个过程非常简单。 我们可以创建类(它们与C ++或其他任何基于类的语言中的类不同,只是在原型继承之上的语法糖),然后从其他类派生新的类。
下面是我们如何在ES6中创建类:
class SmartPhone { constructor(os) { this.os = os; } isAndroid() { return this.os === 'Android'; } isIos() { return this.os === 'iOS'; } };
现在,我们可以创建一个派生自SmartPhone
的新类,如下所示:
class Iphone extends SmartPhone { constructor() { super.call('iOS'); } faceIDScan() {} }
我们不是调用SmartPhone.call
,而是调用super.call
。 在内部,JavaScript引擎会自动为我们执行此操作。
最后,我们可以使用Iphone
创建一个对象,如下所示
let x = new Iphone(); x.faceIDScan(); // calling inherited method console.log(x.isIos()): // true
该ES6示例与先前的构造方法示例相同。 但是阅读和理解起来要干净得多。
原文:https://javascript.info/prototype-inheritance
作者:Indermohan Sing
更多编程相关知识,请访问:编程课程!!
Das obige ist der detaillierte Inhalt vonEine Einführung in die Methode zur Implementierung der Vererbung in JavaScript-Prototypen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!