Maison >interface Web >js tutoriel >Expliquer le mécanisme d'héritage Javascript et l'analyse du code source à héritage simple, des compétences superficielles à profondes_javascript

Expliquer le mécanisme d'héritage Javascript et l'analyse du code source à héritage simple, des compétences superficielles à profondes_javascript

WBOY
WBOYoriginal
2016-05-16 15:25:511284parcourir

La plupart des gens ne sont peut-être pas en mesure de comprendre ce problème de manière systématique. Le langage Javascript n'implémente pas bien l'héritage et les ingénieurs doivent eux-mêmes implémenter un mécanisme d'héritage complet. Ci-dessous, nous maîtriserons les techniques d'utilisation de l'héritage JavaScript du système le moins profond au système le plus profond.

1. Utilisez directement la chaîne prototype

Il s'agit de la méthode la plus simple et la plus grossière et ne peut pas être utilisée dans des projets spécifiques. Une démo simple est la suivante :

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
}
function SubType(){
  this.subproperty = false;
}
//继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
  return this.subproperty;
}
var instance = new SubType();

Le problème avec cette approche est que les propriétés du prototype seront partagées par toutes les instances. La modification d'une propriété héritée via une instance affectera les autres instances. ,Il ne s'agit évidemment pas d'un héritage au sens conventionnel du terme.

2. Utiliser le constructeur

Le constructeur n'est essentiellement qu'une fonction, qui peut être appelée dans n'importe quelle portée. En appelant le constructeur parent dans le constructeur enfant, un héritage simple peut être obtenu.

function SuperType(){
  this.colors = {"red","blue","green"}
}
function SubType(){
  SuperType.call(this);  
}
var instance = new SubType();

Cette implémentation évite le problème du partage d'attributs par plusieurs instances, mais de nouveaux problèmes surviennent, tels que les fonctions ne peuvent pas être partagées et l'instance d'instance de SuperType est fausse.

3. Utiliser des prototypes et des constructeurs en combinaison

function SuperType(name){
  this.name = name;
  this.colors = {"red","blue","green"}
}
SuperType.prototype.sayName = function(){
  //code
}
function SubType(name,age){
  SuperType.call(this,name); 
  this.age = age;
}
SubType.prototype = new SuperType();
var instance = new SubType();

L'utilisation combinée de prototypes et de constructeurs est le modèle d'héritage le plus couramment utilisé en JavaScript. Grâce à cette approche, chaque instance possède ses propres propriétés, tandis que les méthodes du prototype peuvent être partagées. Mais l’inconvénient de cette approche est que quelle que soit la situation, le constructeur de la superclasse sera appelé deux fois. Une fois lors de la création du prototype de sous-classe et une autre fois dans le constructeur de sous-classe. Comment résoudre ce problème ?

4. Héritage combiné parasite

Le prototype de SubType ne doit pas nécessairement être une instance de SuperType, il doit seulement être un objet ordinaire dont le prototype constructeur est le prototype de SuperType. Voici comment Douglas Crockford procède :

function obejct(o){
  function F(){};
  F.prototype = o;
  return new F();
}

En fait, il s'agit de l'implémentation d'Object.create dans ES5. Ensuite nous pouvons modifier la troisième option dans cet article :

function inheritPrototype(subType,superType){
  var prototype = object(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}
function SuperType(name){
  this.name = name;
  this.colors = {"red","blue","green"}
}
SuperType.prototype.sayName = function(){
  //code
}
function SubType(name,age){
  SuperType.call(this,name); 
  this.age = age;
}
inheritPrototype(SubType,SuperType);
var instance = new SubTYpe();

En fait, l'héritage combiné parasite est déjà un très bon mécanisme de mise en œuvre de l'héritage, suffisant pour un usage quotidien. Et si on mettait en avant des exigences plus élevées : par exemple, comment appeler la méthode de la classe parent dans la sous-classe ?

5.implémentation d'une bibliothèque à héritage simple

En regardant un code aussi difficile à comprendre, j'ai d'abord refusé, mais après avoir approfondi, j'ai réalisé que les gens formidables sont des gens formidables et que les idées subtiles sont partout. J'ai des commentaires détaillés pour chaque ligne de code. Si vous voulez connaître les détails, assurez-vous de l’étudier en détail et de lire chaque ligne. Je pense que la partie la plus exquise de cette implémentation est de réécrire la méthode de la classe parent à la demande. Dans l'objet instance, la méthode du même nom de la classe parent peut être appelée via _super, similaire à l'implémentation de Java.

(function(){
  //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧
  //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式
  var initializing = false,fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  //所有类的基类Class,这里的this一般是window对象
  this.Class = function(){};
  //对基类添加extend方法,用于从基类继承
  Class.extend = function(prop){
    //保存当前类的原型
    var _super = this.prototype;
    //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制)
    initializing = true;
    var prototype = new this();   
    initializing = false;
    //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象
    for(var name in prop){
      //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率
      prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name])?
       (function(name,fn){
        return function(){
          //_super在这里是我们的关键字,需要暂时存储一下
          var tmp = this._super; 
          //这里就可以通过this._super调用父类的构造函数了       
          this._super = _super[name];
          //调用子类函数 
          fn.apply(this,arguments);
          //复原_super,如果tmp为空就不需要复原了
          tmp && (this._super = tmp);
        }
       })(name,prop[name]) : prop[name];
    }
    //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应
    function Class(){
      if(!initializing && this.init){
        this.init.apply(this,arguments);  
      }
    }    
    //给子类设置原型
    Class.prototype = prototype;
    //给子类设置构造函数
    Class.prototype.constructor = Class;
    //设置子类的extend方法,使得子类也可以通过extend方法被继承
    Class.extend = arguments.callee;
    return Class;
  }
})();

En utilisant la bibliothèque simple-inheritance, nous pouvons implémenter l'héritage de manière très simple. Le trouvez-vous particulièrement similaire à l'héritage d'un langage fortement typé ?

var Human = Class.extend({
 init: function(age,name){
  this.age = age;
  this.name = name;
 },
 say: function(){
  console.log("I am a human");
 }
});
var Man = Human.extend({
  init: function(age,name,height){
    this._super(age,name);
    this.height = height;
  }, 
  say: function(){
    this._super();
    console.log("I am a man"); 
  }
});
var man = new Man(21,'bob','191');
man.say();

Expliquez le mécanisme d'héritage Javascript et l'analyse du code source par héritage simple, du moins profond au plus profond. J'espère que partager cet article pourra aider tout le monde.

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