Chaque objet en JavaScript a un attribut _proto_ intégré. Cet attribut n'est pas visible pour la programmation. Un objet ou une référence à <.>nul.
Lorsqu'un objet fait référence à un attribut, le moteur JavaScript le recherchera d'abord dans la propre table d'attributs de l'objet. S'il est trouvé, il effectuera la correspondance. opérations de lecture et d'écriture. S'il n'est pas trouvé dans sa propre table attributaire, recherchez dans la table attributaire de l'objet référencé par l'attribut _proto_
, et ainsi de suite jusqu'à ce que l'attribut soit trouvé ou le attribuez des points à Jusqu'à _proto_
. null
La chaîne de référence de est appelée la _proto_
chaîne prototype.
Notez qu'il y a ici un problème d'optimisation des performances : plus vous recherchez profondément dans la chaîne de prototypes, plus cela prend de temps.
Chaîne et constructeur de prototypes
. La fonction en JavaScript a un attribut
Cet attribut prototype
est un objet, et l'un de ses attributs est prototype
. Référencez la fonction elle-même. C'est-à-dire : constructor
func.prototype.constructor === func; // ==> true
La propriété new
de cet objet fait référence à la propriété _proto_
prototype
de son constructeur. Donc ce n'est pas difficile à comprendre :
var obj = new Func(); obj.constructor == Func; // ==> true
obj instanceof Func; // ==> true
sur la chaîne prototype. Les propriétés constructor
sont des références au même objet _proto_
. Ainsi, la modification de l'objet prototype
affectera toutes les instances. prototype
Plusieurs façons d'implémenter l'héritage des "sous-classes"
Cependant, les programmeurs familiers avec des langages tels que Java espèrent également qu'en utilisant JavaScript, ils pourront générer des instances via des classes et réutiliser le code via des sous-classes, de la même manière qu'avec Java. Alors avant ES6, comment utiliser une méthode de type « classe » comme le code suivant ?
var parent = new Parent("Sam");var child = new Children("Samson"); parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"child instanceof Parent; // ==> true
Discutons de plusieurs façons de l'implémenter :
La manière la plus simple
function Parent(name){this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); }function Children(name){this.name = name; } Children.prototype = new Parent(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
de cet objet sont complètement inutiles. name
La première amélioration
// ...Children.prototype = Parent.prototype;// ...
Cependant, dans ce cas, les prototypes de la sous-classe et de la classe parent font référence au même objet, et la modification du
de la sous-classe affectera également le prototype de la classe parent. prototype
A cette époque nous avons découvert :
parent.say(); // ==> "Hello,Sam! hoo~~"
La deuxième amélioration - constructeur temporaire/
Object.create()function F(){ // empty } F.prototype = Parent.prototype; Children.prototype = new F();// ...parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"
de F
et ne change pas , résolvant ainsi le problème ci-dessus. Parent.prototype
A l'ère de
, on peut aussi le faire directement :
这里的思路是一样的,都是让子类的prototype
不直接引用父类prototype
。目前的现代浏览器几乎已经添加了对这个方法的支持。(但我们下面会仍以临时构造函数为基础)
但是细细思考,这个方案仍有需要优化的地方。例如:如何让父类的构造函数逻辑直接运用到子类中,而不是再重新写一遍一样的?这个例子中只有一个name
属性的初始化,那么假设有很多属性且逻辑一样的话,岂不是没有做到代码重用?
使用apply/call
,实现“方法重用”的思想。
function Children(name){ Parent.apply(this, arguments);// do other initial things}
现在完整的代码如下:
function Parent(name){this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); }function Children(name){ Parent.apply(this, arguments);// do other initial things}function F(){ // empty } F.prototype = Parent.prototype; Child.prototype = new F(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
这就是所谓“圣杯”模式,听着很高大上吧?
以上就是ES3的时代,我们用来实现原型继承的一个近似最佳实践。
“圣杯”模式依然存在一个问题:虽然父类和子类实例的继承的prototype
对象不是同一个实例,但是这两个prototype
对象上面的属性引用了同样的对象。
假设我们有:
Parent.prototype.a = { x: 1};// ...
那么即使是“圣杯”模式下,依然会有这样的问题:
parent.x // ==> 1child.x // ==> 1child.x = 2; parent.x // ==>2
问题在于,JavaScript的拷贝不是 深拷贝(deepclone)
要解决这个问题,我们可以利用属性递归遍历,自己实现一个深拷贝的方法。这个方法在这里我就不写了。
ES6极大的支持了工程化,它的标准让浏览器内部实现类和类的继承:
class Parent { constructor(name) { //构造函数 this.name = name; } say() { console.log("Hello, " + this.name + "!"); } } class Children extends Parent { constructor(name) { //构造函数super(name); //调用父类构造函数// ... } say() { console.log("Hello, " + this.name + "! hoo~~"); } }
从此走上强类型的不归路。。。
上张顿悟图
什么?还不明白?!麻烦出门左拐。推荐阮老师JavaScript万物诞生记。
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!