JavaScript の継承メカニズムと単純継承のソース コード分析を浅いから深い JavaScript スキルまで説明する

WBOY
リリース: 2016-05-16 15:25:51
オリジナル
1247 人が閲覧しました

ほとんどの人はこの問題を体系的に理解できないかもしれません。 Javascript 言語は継承を適切に実装していないため、エンジニアは完全な継承メカニズムを自分で実装する必要があります。次に、JavaScript 継承を浅いシステムから深いシステムに使用するスキルを習得します。

1. プロトタイプチェーンを直接使用します

これは最も単純かつ粗雑な方法であり、特定のプロジェクトではほとんど使用できません。簡単なデモは次のとおりです:

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();
ログイン後にコピー

このアプローチの問題は、プロトタイプ内のプロパティがすべてのインスタンスで共有されることになり、1 つのインスタンスを通じて継承されたプロパティを変更すると、他のインスタンスに影響を与えることです。 ,これは明らかに、従来の意味での継承ではありません。

2. コンストラクター

を使用します。

コンストラクターは本質的には単なる関数であり、子コンストラクター内で親コンストラクターを呼び出すことで、単純な継承を実現できます。

function SuperType(){
  this.colors = {"red","blue","green"}
}
function SubType(){
  SuperType.call(this);  
}
var instance = new SubType();
ログイン後にコピー

この実装により、複数のインスタンスが属性を共有する問題は回避されますが、関数が共有できない、インスタンスのinstanceof SuperTypeがfalseになるなど、新たな問題が発生します。

3. プロトタイプとコンストラクターを組み合わせて使用​​する

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();
ログイン後にコピー

プロトタイプとコンストラクターの組み合わせは、JavaScript で最も一般的に使用される継承パターンです。このアプローチを使用すると、各インスタンスは独自のプロパティを持ち、プロトタイプのメソッドを共有できます。ただし、このアプローチの欠点は、状況に関係なく、スーパークラス コンストラクターが 2 回呼び出されることです。 1 回目はサブクラスのプロトタイプを作成するとき、もう 1 回はサブクラスのコンストラクター内で行います。この問題を解決するにはどうすればよいでしょうか?

4. 寄生結合継承

SubType のプロトタイプは SuperType のインスタンスである必要はありません。コンストラクター プロトタイプが SuperType のプロトタイプである通常のオブジェクトであれば十分です。ダグラス・クロックフォードのやり方は次のとおりです:

function obejct(o){
  function F(){};
  F.prototype = o;
  return new F();
}
ログイン後にコピー

実際、これは ES5 の Object.create の実装です。次に、この記事の 3 番目のオプションを変更できます:

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();
ログイン後にコピー

実際、寄生結合継承はすでに非常に優れた継承実装メカニズムであり、日常使用には十分です。たとえば、サブクラスで親クラスのメソッドを呼び出す方法など、より高い要件を提示したらどうなるでしょうか?

5.単純継承ライブラリの実装

こんなわかりにくいコードを見て、最初は拒否しましたが、深く掘り下げていくと、偉い人は偉いし、微妙なアイデアがいたるところにあることに気づきました。コードの各行に詳細なコメントが付いています。詳細を知りたい場合は、必ず詳細に調べて、すべての行を読んでください。この実装の最も絶妙な点は、Java の実装と同様に、インスタンス オブジェクト内で親クラスの同名のメソッドを _super 経由で呼び出せるようにしていることだと思います。

(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;
  }
})();
ログイン後にコピー

単純継承ライブラリを使用すると、非常に簡単な方法で継承を実装できます。これは、厳密に型指定された言語の継承に特に似ていると思いますか?

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();
ログイン後にコピー

JavaScript の継承メカニズムと単純継承のソース コード分析を浅いところから深いところまで説明し、この記事を共有することで皆さんのお役に立てれば幸いです。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート