Maison  >  Article  >  interface Web  >  Comment implémenter des fonctionnalités d'héritage dans les programmes JavaScript (tutoriel graphique)

Comment implémenter des fonctionnalités d'héritage dans les programmes JavaScript (tutoriel graphique)

亚连
亚连original
2018-05-21 09:33:35844parcourir

JavaScript est un langage qui prétend être orienté objet, et l'héritage est une caractéristique majeure de l'orientation objet. Voici ce que j'ai compilé pour vous.

Vue d'ensembleTous les objets en JavaScript ont leur propre chaîne d'héritage. Autrement dit, chaque objet hérite d’un autre objet, appelé objet « prototype ». À l'exception de null, il n'a pas son propre objet prototype.

L'importance de l'objet prototype est que si l'objet A est le prototype de l'objet B, alors l'objet B peut obtenir toutes les propriétés et méthodes de l'objet A. La méthode Object.getPrototypof est utilisée pour obtenir l'objet prototype de l'objet actuel.

var p = Object.getPrototypeOf(obj);

Dans le code ci-dessus, l'objet p est l'objet prototype de l'objet obj.

La méthode Object.create est utilisée pour générer un nouvel objet et hériter de l'objet spécifié.

var obj = Object.create(p);

Dans le code ci-dessus, le prototype de l'objet obj nouvellement généré est l'objet p.

L'attribut non standard __proto__ (deux traits de soulignement avant et après) peut remplacer l'objet prototype d'un objet. Cependant, vous devez utiliser cet attribut le moins possible et utiliser plutôt Object.getPrototypeof() et Object.setPrototypeOf() pour lire et écrire des objets prototypes.

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true

Le code ci-dessus définit l'objet p comme prototype de l'objet obj via l'attribut __proto__.

Voici un exemple pratique.

var a = {x: 1};
var b = {__proto__: a};
b.x // 1

Dans le code ci-dessus, l'objet b définit son objet prototype sur l'objet a via l'attribut __proto__, afin que l'objet b puisse obtenir tous les attributs et méthodes de l'objet a. L'objet b lui-même n'a pas d'attribut x, mais le moteur JavaScript trouve son objet prototype a via l'attribut __proto__, puis lit l'attribut x de a. La commande

new crée un nouvel objet instance via le constructeur. L'essence est de lier le prototype de l'objet instance à l'attribut prototype du constructeur, puis d'exécuter le constructeur sur l'objet instance.

var o = new Foo();

// 等同于
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

Le propre attribut __proto__ de l'objet prototype peut également pointer vers d'autres objets, formant ainsi une "chaîne de prototypes" niveau par niveau.

var a = { x: 1 };
var b = { __proto__: a };
var c = { __proto__: b };

c.x // 1

Il est à noter que la recherche d'un certain attribut dans la chaîne de prototypes d'un niveau supérieur aura un impact sur les performances. Les propriétés que vous recherchez se trouvent dans l'objet prototype de niveau supérieur, plus l'impact sur les performances est important. Si vous recherchez une propriété qui n'existe pas, toute la chaîne de prototypes sera parcourue.

Cette action pointe vers Peu importe où elle est définie, lorsqu'elle est utilisée, elle pointe toujours vers l'objet actuel, pas vers l'objet prototype.

var o = {
 a: 2,
 m: function(b) {
  return this.a + 1;
 }
};

var p = Object.create(o);
p.a = 12;

p.m() // 13

Dans le code ci-dessus, la méthode m de l'objet p provient de son objet prototype o. À l’heure actuelle, l’objet this à l’intérieur de la méthode m ne pointe pas vers o, mais vers p.

Héritage du constructeurCette section explique comment faire en sorte qu'un constructeur hérite d'un autre constructeur.

Supposons qu'il existe un constructeur Shape.

function Shape() {
 this.x = 0;
 this.y = 0;
}

Shape.prototype.move = function (x, y) {
 this.x += x;
 this.y += y;
 console.info('Shape moved.');
};
Rectangle构造函数继承Shape。

function Rectangle() {
 Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
 this.base = Shape;
 this.base();
}

// 子类继承父类的方法
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle // true
rect instanceof Shape // true

rect.move(1, 1) // 'Shape moved.'

Le code ci-dessus indique que l'héritage du constructeur est divisé en deux parties. Une partie est que la sous-classe appelle la méthode constructeur de la classe parent, et l'autre partie est que le prototype de la sous-classe. pointe vers le prototype de la classe parent.

Dans le code ci-dessus, la sous-classe hérite de la classe parent dans son ensemble. Parfois, seul l’héritage d’une seule méthode est nécessaire. Dans ce cas, la méthode d’écriture suivante peut être utilisée.

ClassB.prototype.print = function() {
 ClassA.prototype.print.call(this);
 // some code
}

Dans le code ci-dessus, la méthode print de la sous-classe B appelle d'abord la méthode print de la classe parent A, puis déploie son propre code. Cela équivaut à hériter de la méthode print de la classe parent A.

Attribut __proto__ L'attribut __proto__ pointe vers l'objet prototype de l'objet actuel, c'est-à-dire l'attribut prototype du constructeur.

var obj = new Object();

obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true

Le code ci-dessus crée d'abord un nouvel objet obj, et son attribut __proto__ pointe vers l'attribut prototype du constructeur (Object ou obj.constructor). Par conséquent, après avoir comparé les deux, true est renvoyé.

Par conséquent, il existe trois méthodes pour obtenir l'objet prototype de l'objet instance obj.

  • obj.__proto__

  • obj.constructor.prototype

  • Object.getPrototypeOf(obj)

Parmi les trois méthodes ci-dessus, les deux premières ne sont pas très fiables. La dernière norme ES6 stipule que l'attribut __proto__ doit uniquement être déployé dans les navigateurs et n'a pas besoin d'être déployé dans d'autres environnements. Obj.constructor.prototype peut devenir invalide lors de la modification manuelle de l'objet prototype.

var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;
var c = new C();

c.constructor.prototype === p // false

Dans le code ci-dessus, l'objet prototype du constructeur C a été remplacé par p et, par conséquent, c.constructor.prototype a été déformé. Par conséquent, lors de la modification de l'objet prototype, vous devez généralement définir l'attribut constructeur en même temps.

C.prototype = p;
C.prototype.constructor = C;

c.constructor.prototype === p // true

Par conséquent, il est recommandé d'utiliser la troisième méthode Object.getPrototypeOf pour obtenir l'objet prototype. La méthode est utilisée comme suit.

var o = new Object();

Object.getPrototypeOf(o) === Object.prototype
// true

Vous pouvez utiliser la méthode Object.getPrototypeOf pour vérifier si le navigateur prend en charge l'attribut __proto__. Les navigateurs plus anciens ne prennent pas en charge cet attribut.

Object.getPrototypeOf({ __proto__: null }) === null

Le code ci-dessus définit l'attribut __proto__ d'un objet sur null, puis utilise la méthode Object.getPrototypeOf pour obtenir le prototype de l'objet afin de déterminer s'il est égal à null. Si l'environnement actuel prend en charge l'attribut __proto__, le résultat de la comparaison entre les deux devrait être vrai.

Avec l'attribut __proto__, vous pouvez facilement définir le prototype de l'objet instance. Supposons qu'il y ait trois objets machine, véhicule et voiture, où machine est le prototype du véhicule et véhicule est le prototype de la voiture. Il suffit de deux lignes de code pour le configurer.

vehicle.__proto__ = machine;
car.__proto__ = vehicle;

Ce qui suit est un exemple de lecture des attributs définis sur l'objet prototype via l'attribut __proto__ et l'attribut constructor.prototype.

Array.prototype.p = 'abc';
var a = new Array();

a.__proto__.p // abc
a.constructor.prototype.p // abc

Évidemment, __proto__ semble plus simple.

通过构造函数生成实例对象时,实例对象的__proto__属性自动指向构造函数的prototype对象。

var f = function (){};
var a = {};

f.prototype = a;
var o = new f();

o.__proto__ === a
// true

属性的继承属性分成两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。

对象的原生属性对象本身的所有属性,可以用Object.getOwnPropertyNames方法获得。

Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]

对象本身的属性之中,有的是可以枚举的(enumerable),有的是不可以枚举的。只获取那些可以枚举的属性,使用Object.keys方法。

Object.keys(Date) // []
hasOwnProperty()

hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

Date.hasOwnProperty('length')
// true

Date.hasOwnProperty('toString')
// false

hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。

对象的继承属性用Object.create方法创造的对象,会继承所有原型对象的属性。

var proto = { p1: 123 };
var o = Object.create(proto);

o.p1 // 123
o.hasOwnProperty("p1") // false

获取所有属性判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。

"length" in Date // true
"toString" in Date // true

获得对象的所有可枚举属性(不管是自身的还是继承的),可以使用for-in循环。

var o1 = {p1: 123};

var o2 = Object.create(o1,{
 p2: { value: "abc", enumerable: true }
});

for (p in o2) {console.info(p);}
// p2
// p1

为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。

for ( var name in object ) {
 if ( object.hasOwnProperty(name) ) {
  /* loop code */
 }
}

获得对象的所有属性(不管是自身的还是继承的,以及是否可枚举),可以使用下面的函数。

function inheritedPropertyNames(obj) {
 var props = {};
 while(obj) {
  Object.getOwnPropertyNames(obj).forEach(function(p) {
   props[p] = true;
  });
  obj = Object.getPrototypeOf(obj);
 }
 return Object.getOwnPropertyNames(props);
}

用法如下:

inheritedPropertyNames(Date)
// ["caller", "constructor", "toString", "UTC", "call", "parse", "prototype", "__defineSetter__", "__lookupSetter__", "length", "arguments", "bind", "__lookupGetter__", "isPrototypeOf", "toLocaleString", "propertyIsEnumerable", "valueOf", "apply", "__defineGetter__", "name", "now", "hasOwnProperty"]

对象的拷贝如果要拷贝一个对象,需要做到下面两件事情。

确保拷贝后的对象,与原对象具有同样的prototype原型对象。
确保拷贝后的对象,与原对象具有同样的属性。
下面就是根据上面两点,编写的对象拷贝的函数。

function copyObject(orig) {
 var copy = Object.create(Object.getPrototypeOf(orig));
 copyOwnPropertiesFrom(copy, orig);
 return copy;
}

function copyOwnPropertiesFrom(target, source) {
 Object
 .getOwnPropertyNames(source)
 .forEach(function(propKey) {
  var desc = Object.getOwnPropertyDescriptor(source, propKey);
  Object.defineProperty(target, propKey, desc);
 });
 return target;
}

多重继承JavaScript不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

function M1(prop) {
 this.hello = prop;
}

function M2(prop) {
 this.world = prop;
}

function S(p1, p2) {
 this.base1 = M1;
 this.base1(p1);
 this.base2 = M2;
 this.base2(p2);
}
S.prototype = new M1();

var s = new S(111, 222);
s.hello // 111
s.world // 222

上面代码中,子类S同时继承了父类M1和M2。当然,从继承链来看,S只有一个父类M1,但是由于在S的实例上,同时执行M1和M2的构造函数,所以它同时继承了这两个类的方法。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细介绍在JS中Map和ForEach的区别

js装饰设计模式学习心得(详细解答)

详解解读JS数值Number类型(图文教程)

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn