JavaScript の継承について説明すると約束していましたが、今のところ遅れています。早速、本題に入りましょう。
継承を理解したいということは、オブジェクト指向 JavaScript をある程度理解していることを意味します。まだ理解できない場合は、「オブジェクト指向 JS の基本的な説明」を参照してください。ファクトリモード、コンストラクターモード、プロトタイプモード、混合モード、動的プロトタイプモード》、JavaScriptの継承を完了するために一般的に使用されるメソッドについて話しましょう。
プロトタイプチェーン
JavaScript で継承を実装する最も簡単な方法は、プロトタイプ チェーンを使用することです。つまり、「subtype.prototype = newparent type ();」というように、サブタイプのプロトタイプを親タイプのインスタンスにポイントします。実装方法は以下の通りです:
// 为父类型创建构造函数 function SuperType() { this.name = ['wuyuchang', 'Jack', 'Tim']; this.property = true; } // 为父类型添加方法 SuperType.prototype.getSuerperValue = function() { return this.property; } // 为子类型创建构造函数 function SubType() { this.test = ['h1', 'h2', 'h3', 'h4']; this.subproperty = false; } // 实现继承的关键步骤,子类型的原型指向父类型的实例 SubType.prototype = new SuperType(); // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空 SubType.prototype.getSubValue = function() { return this.subproperty; } /* 以下为测试代码示例 */ var instance1 = new SubType(); instance1.name.push('wyc'); instance1.test.push('h5'); alert(instance1.getSuerperValue()); // true alert(instance1.getSubValue()); // false alert(instance1.name); // wuyuchang,Jack,Tim,wyc alert(instance1.test); // h1,h2,h3,h4,h5 var instance2 = new SubType(); alert(instance2.name); // wuyuchang,Jack,Tim,wyc alert(instance2.test); // h1,h2,h3,h4
上記のコードはプロトタイプ チェーンを通じて実装された単純な継承であることがわかりますが、テスト コードの例にはまだいくつかの問題があります。私のブログ記事「オブジェクト指向JS、ファクトリモード、コンストラクタモード、プロトタイプモード、ハイブリッドモード、ダイナミックプロトタイプモードの基本解説」を読んだ子供たちは、プロトタイプチェーンコードの存在を知っているはずです最初の問題は、サブタイプのプロトタイプが親タイプのインスタンス、つまり、サブタイプのプロトタイプに含まれる親タイプの属性であるため、参照型値のプロトタイプ属性がすべてのインスタンス によって共有されます。上記のコードのinstance1.name.push('wyc'); は、この問題の存在を証明できます。プロトタイプ チェーンの 2 番目の問題は次のとおりです。 サブタイプのインスタンスを作成するとき、パラメーター をスーパータイプのコンストラクターに渡すことができません。したがって、実際の開発ではプロトタイプチェーンのみを使用することはほとんどありません。
コンストラクターを借用
プロトタイプ チェーンに存在する 2 つの問題を解決するために、開発者は借用コンストラクターと呼ばれる手法を使用してプロトタイプ チェーンに存在する問題を解決し始めました。このテクノロジーの実装アイデアも非常にシンプルで、サブタイプのコンストラクター内で親タイプのコンストラクターを呼び出すだけです。関数は特定の環境でコードを実行する単なるオブジェクトであるため、apply() メソッドまたは call() メソッドを介して コンストラクター を実行できることを忘れないでください。コードは次のとおりです:
// 为父类型创建构造函数 function SuperType(name) { this.name = name; this.color = ['pink', 'yellow']; this.property = true; this.testFun = function() { alert('http://tools.jb51.net/'); } } // 为父类型添加方法 SuperType.prototype.getSuerperValue = function() { return this.property; } // 为子类型创建构造函数 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; this.subproperty = false; } // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空 SubType.prototype.getSubValue = function() { return this.subproperty; } /* 以下为测试代码示例 */ var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']); instance1.name.push('hello'); instance1.test.push('h5'); instance1.color.push('blue'); instance1.testFun(); // http://tools.jb51.net/ alert(instance1.name); // wuyuchang,Jack,Nick,hello // alert(instance1.getSuerperValue()); // error 报错 alert(instance1.test); // h1,h2,h3,h4,h5 alert(instance1.getSubValue()); // false alert(instance1.color); // pink,yellow,blue var instance2 = new SubType('wyc'); instance2.testFun(); // http://tools.jb51.net/ alert(instance2.name); // wyc // alert(instance2.getSuerperValue()); // error 报错 alert(instance2.test); // h1,h2,h3,h4 alert(instance2.getSubValue()); // false alert(instance2.color); // pink,yellow
上記のコードでは、サブタイプ SubType のコンストラクターがスーパータイプ「SuperType.call(this, name);」を呼び出していることがわかり、作成時にプロパティの継承を実現することもできます。サブタイプのインスタンスが渡されますが、新たな問題が再び発生します。親型のコンストラクターにメソッド testFun を定義し、親型のプロトタイプにメソッド getSuperValue を定義していることがわかります。ただし、 がサブタイプをインスタンス化した後でも、親型のプロトタイプに定義されているメソッド getSuperValue を呼び出すことはできません。親型のコンストラクター メソッド testFun のみを呼び出すことができます。 。これは、オブジェクトの作成時にコンストラクター パターンのみを使用するのと同じであり、関数が再利用できなくなります。これらの問題を考慮すると、コンストラクターを借用する手法が単独で使用されることはほとんどありません。
結合継承 (プロトタイプチェーン借用コンストラクター)
名前が示すように、結合継承は、プロトタイプ チェーンの使用とコンストラクターの借用の利点を組み合わせたパターンです。実装も非常に簡単です。組み合わせであるため、プロトタイプチェーン継承メソッド とコンストラクター継承属性 の両方の利点を確実に組み合わせています。具体的なコードの実装は次のとおりです:
// 为父类型创建构造函数 function SuperType(name) { this.name = name; this.color = ['pink', 'yellow']; this.property = true; this.testFun = function() { alert('http://tools.jb51.net/'); } } // 为父类型添加方法 SuperType.prototype.getSuerperValue = function() { return this.property; } // 为子类型创建构造函数 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; this.subproperty = false; } SubType.prototype = new SuperType(); // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空 SubType.prototype.getSubValue = function() { return this.subproperty; } /* 以下为测试代码示例 */ var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']); instance1.name.push('hello'); instance1.test.push('h5'); instance1.color.push('blue'); instance1.testFun(); // http://tools.jb51.net/ alert(instance1.name); // wuyuchang,Jack,Nick,hello alert(instance1.getSuerperValue()); // true alert(instance1.test); // h1,h2,h3,h4,h5 alert(instance1.getSubValue()); // false alert(instance1.color); // pink,yellow,blue var instance2 = new SubType('wyc'); instance2.testFun(); // http://tools.jb51.net/ alert(instance2.name); // wyc alert(instance2.getSuerperValue()); // true alert(instance2.test); // h1,h2,h3,h4 alert(instance2.getSubValue()); // false alert(instance2.color); // pink,yellow
上記のコードは、SuperType.call(this, name); を通じて親型のプロパティを継承し、SubType.prototype = new SuperType(); を通じて親型のメソッドを継承します。上記のコードは、プロトタイプ チェーンと借用コンストラクターが遭遇する問題を都合よく解決し、JavaScript で最も一般的に使用されるインスタンス継承メソッドになっています。ただし、混合モードには欠点がないわけではありません。上記のコードでは、メソッドを継承するときに、実際には親型のプロパティが継承されていることがわかります。ただし、この時点では参照型が共有されているため、それが呼び出されます。サブタイプのコンストラクターで 2 回呼び出すと、親型のコンストラクターは親型のプロパティを継承し、プロトタイプで継承されたプロパティを上書きします。コンストラクターを 2 回呼び出す必要はありませんが、これを解決する方法はありますか?この問題を解く際に、次の2つのパターンを見てみましょう。
プロトタイプの継承
プロトタイプ継承の実装方法は、通常の継承とは異なります。プロトタイプ継承では、厳密な意味ではコンストラクターを使用せず、カスタム型を作成せずに、既存のオブジェクトに基づいて新しいオブジェクトを作成します。 。具体的なコードは次のとおりです:
function object(o) { function F() {} F.prototype = o; return new F(); }
コード例:
/* 原型式继承 */ function object(o) { function F() {} F.prototype = o; return new F(); } var person = { name : 'wuyuchang', friends : ['wyc', 'Nicholas', 'Tim'] } var anotherPerson = object(person); anotherPerson.name = 'Greg'; anotherPerson.friends.push('Bob'); var anotherPerson2 = object(person); anotherPerson2.name = 'Jack'; anotherPerson2.friends.push('Rose'); alert(person.friends); // wyc,Nicholas,Tim,Bob,Rose
寄生継承
/* 寄生式继承 */ function createAnother(original) { var clone = object(original); clone.sayHi = function() { alert('hi'); } return clone; }
使用例:
/* 原型式继承 */ function object(o) { function F() {} F.prototype = o; return new F(); } /* 寄生式继承 */ function createAnother(original) { var clone = object(original); clone.sayHi = function() { alert('hi'); } return clone; } var person = { name : 'wuyuchang', friends : ['wyc', 'Nicholas', 'Rose'] } var anotherPerson = createAnother(person); anotherPerson.sayHi();
寄生组合式继承
前面说过了JavaScrip中组合模式实现继承的缺点,现在我们就来解决它的缺点,实现思路是,对于构造函数继承属性,而原型链的混成形式继承方法,即不用在继承方法的时候实例化父类型的构造函数。代码如下:
function object(o) { function F() {} F.prototype = o; return new F(); } /* 寄生组合式继承 */ function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
而在使用时只需要将组合模式中的“SubType.prototype = new SuperType();”这行代码替换成inheritPrototype(subType, superType);即可。寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性。与此同时,原型链还能保持不变,因此,还能够正常使用instanceof和isPrototypeof()。这也是目前来说最理想的继承方式了,目前也在向这种模式转型。(YUI也使用了这种模式。)
此博文参考《JavaScript高级程序设计第3版》,代码为经过改写,更具体,并加了注释使大家更易懂。如对JS继承方面有独到见解的童鞋不别吝啬,回复您的见解供大家参考!