Dans cet article, nous discuterons des prototypes et de la façon de les utiliser pour l'héritage dans JS. Nous verrons également en quoi les méthodes prototypes diffèrent de l'héritage basé sur les classes.
L'héritage est une caractéristique distinctive des langages de programmation qui a émergé avec l'introduction des langages de programmation orientés objet. La plupart de ces langages sont des langages basés sur des classes. Ici, la classe est comme un plan et l’objet est sa représentation. Autrement dit, pour créer un objet, nous devons d’abord créer une classe, puis nous pouvons créer n’importe quel nombre d’objets à partir d’une classe.
Imaginez que nous ayons une classe qui représente un smartphone. Cette classe a des fonctions comme les autres smartphones qui peuvent prendre des photos et disposer d'un positionnement GPS. Ce qui suit est une description d'une telle classe en utilisant C++ :
class SmartPhone { public: void captureImages() {} } SmartPhone x; x.captureImages()
Nous créons une classe nommée SmartPhone
, qui a une méthode nommée capturePictures
pour prendre des photos.
Si nous avons besoin d'une classe iPhone
capable de capturer des images et de certaines fonctionnalités spéciales telles que la numérisation d'identité faciale. Voici deux solutions possibles :
Réécrivez la fonctionnalité de capture d'image dans une nouvelle classe avec d'autres fonctionnalités courantes des smartphones, ainsi que des fonctionnalités spécifiques à l'iPhone. Mais cette approche nécessite plus de temps et d’efforts et introduit davantage de bugs.
SmartPhone
classes C'est le rôle de l'héritage, c'est aussi un moyen de réutiliser des fonctions dans d'autres classes/objets. Voici comment nous héritons de la méthode SmartPhone
de la classe capturePictures
, implémentée en utilisant C++ comme suit :
class Iphone: public SmartPhone { public: void faceIDScan() {} } Iphone x x.faceIDScan() x.captureImages()
Ce qui précède est un exemple d'héritage simple. Cependant, cela montre que l'héritage nous permet de réutiliser le code d'une manière qui rend le programme résultant moins sujet aux erreurs et prend moins de temps à développer.
Voici quelques informations importantes sur les classes :
[[Prototype]]
En JS, tous les objets ont une propriété interne spéciale qui est essentiellement une référence à un autre objet. Cette référence dépend de la manière dont l'objet a été créé. Dans la spécification ECMAScript/JavaScript, il est représenté par
[[Prototype]]
Puisque [[Prototype]]
est lié à un objet, cet objet a sa propre référence
[[Prototype]]
La chaîne
__proto__
[[Prototype]]
Pour accéder au __proto__
d'un objet, la plupart des navigateurs fournissent l'attribut
obj.__proto__
__proto__
En plus de l'attribut [[Prototype]]
, il existe également un moyen standard d'accéder à
Object.getPrototypeOf(obj);
[[Prototype]]
a une méthode similaire. Définir les propriétés : Object.setPrototypeOf(obj, prototype);
[[Prototype]]
.prototype
et [[Prototype]]
.prototype
de l'objet n'est rien de plus qu'une notation standard utilisée pour représenter le prototype d'un objet. De nombreux développeurs le confondent avec l'attribut .prototype
, ce qui est complètement différent. Jetons un coup d'œil à l'attribut
new
En JS, il existe de nombreuses façons de créer des objets. Une façon est d'utiliser le constructeur, appelez-le en utilisant le mot-clé
function SmartPhone(os) { this.os = os } let phone = new SmartPhone('Android')
phone
Imprimez l'objet dans la console :
{ os: "IPhone" __proto__{ constructor: ƒ SmartPhone(os) __proto__: Object } }
phone
Maintenant, si nous voulons 🎜 >Il existe quelques méthodes sur l'objet, nous pouvons utiliser l'attribut .prototype
sur la fonction, comme indiqué ci-dessous : SmartPhone.prototype.isAndroid = function () { return this.os === 'Android' || 'android' }
Lors de la création à nouveau de l'objet phone
, imprimez l'objet phone
comme suit :
{ os: "Android" __proto__{ isAndroid: ƒ() constructor: ƒ SmartPhone(os) __proto__: Object } }
Nous pouvons voir la méthode [[Prototype]]
dans le isAndroid()
de l'objet.
En bref, une propriété .prototype
est fondamentalement comme un plan d'un objet [[Prototype]]
créé par un constructeur donné. Tout ce qui est déclaré dans une propriété/objet .prototype
apparaîtra dans le [[Prototype]]
de l'objet.
En fait, si vous comparez les SmartPhone.prototype 与
téléphones [[Prototype]]
, vous constaterez qu'ils sont identiques :
console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype); // true
Il est à noter que nous pouvons également créer dans la méthode constructeur :
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
更多编程相关知识,请访问:编程课程!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!