私は問題の解決策を見つけるために最後の数時間を費やしましたが、それは絶望的なようです。
基本的に、子クラスから親メソッドを呼び出す方法を知る必要があります。これまで試してきたすべてのことは、機能しないか、親メソッドを上書きすることになります。
次のコードを使用して、javascriptでOOPを設定しています。
// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}
function extend(base, sub) {
// copy the prototype from the base to setup inheritance
surrogateCtor.prototype = base.prototype;
sub.prototype = new surrogateCtor();
sub.prototype.constructor = sub;
}
// parent class
function ParentObject(name) {
this.name = name;
}
// parent's methods
ParentObject.prototype = {
myMethod: function(arg) {
this.name = arg;
}
}
// child
function ChildObject(name) {
// call the parent's constructor
ParentObject.call(this, name);
this.myMethod = function(arg) {
// HOW DO I CALL THE PARENT METHOD HERE?
// do stuff
}
}
// setup the prototype chain
extend(ParentObject, ChildObject);
最初に親のメソッドを呼び出してから、子クラスでさらにいくつかのものを追加する必要があります。
ほとんどのOOP言語では、parent.myMethod()
を呼び出すのと同じくらい簡単ですが、javascriptでどのように実行されるのか本当に理解できません。
どんな助けも大歓迎です、ありがとう!
以下にその方法を示します。ParentClass.prototype.myMethod();
または、現在のインスタンスのコンテキストで呼び出す場合は、次のようにします:ParentClass.prototype.myMethod.call(this)
引数を使用して子クラスから親メソッドを呼び出す場合も同様です:ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)
* ヒント:apply()
の代わりにcall()
を使用して、引数を配列として渡します
ES6スタイルでは、super
キーワードなどの新機能を使用できます。 super
キーワードは、ES6クラスの構文を使用している場合、親クラスのコンテキストに関するものです。非常に簡単な例として、チェックアウト:
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod(); // 'hello, too'
また、super
を使用して親コンストラクターを呼び出すことができます。
class Foo {}
class Bar extends Foo {
constructor(num) {
let tmp = num * 2; // OK
this.num = num; // ReferenceError
super();
this.num = num; // OK
}
}
そしてもちろん、これを使用して親クラスのプロパティsuper.prop
にアクセスできます。したがって、ES6を使用して幸せになります。
これを行うために、ES6のClass
抽象化に制限されません。親コンストラクタのプロトタイプメソッドへのアクセスは、__proto__
プロパティ(これは減価されていると不満を言う仲間のJSコーダーがいると確信しています)を通じて可能です。 (特にArrayサブクラス化のニーズのため)。 __proto__
プロパティは、私が知っているすべての主要なJSエンジンで引き続き使用可能ですが、ES6はその上に Object.getPrototypeOf()
機能を導入しました。 Class
抽象化のsuper()
ツールは、この構文糖衣です。
したがって、親コンストラクターの名前にアクセスできず、Class
抽象化を使用したくない場合は、次のようにすることができます。
function ChildObject(name) {
// call the parent's constructor
ParentObject.call(this, name);
this.myMethod = function(arg) {
//this.__proto__.__proto__.myMethod.call(this,arg);
Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
}
}
多重継承レベルの場合、この関数は他の言語のsuper()メソッドとして使用できます。 ここにデモフィドルがあります 、いくつかのテストでは、メソッドuse内でこのように使用できます:call_base(this, 'method_name', arguments);
それはごく最近のES機能を利用しており、古いブラウザとの互換性は保証されていません。 IE11、FF29、CH35でテスト済み。
/**
* Call super method of the given object and method.
* This function create a temporary variable called "_call_base_reference",
* to inspect whole inheritance linage. It will be deleted at the end of inspection.
*
* Usage : Inside your method use call_base(this, 'method_name', arguments);
*
* @param {object} object The owner object of the method and inheritance linage
* @param {string} method The name of the super method to find.
* @param {array} args The calls arguments, basically use the "arguments" special variable.
* @returns {*} The data returned from the super method.
*/
function call_base(object, method, args) {
// We get base object, first time it will be passed object,
// but in case of multiple inheritance, it will be instance of parent objects.
var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
// We get matching method, from current object,
// this is a reference to define super method.
object_current_method = base[method],
// Temp object wo receive method definition.
descriptor = null,
// We define super function after founding current position.
is_super = false,
// Contain output data.
output = null;
while (base !== undefined) {
// Get method info
descriptor = Object.getOwnPropertyDescriptor(base, method);
if (descriptor !== undefined) {
// We search for current object method to define inherited part of chain.
if (descriptor.value === object_current_method) {
// Further loops will be considered as inherited function.
is_super = true;
}
// We already have found current object method.
else if (is_super === true) {
// We need to pass original object to apply() as first argument,
// this allow to keep original instance definition along all method
// inheritance. But we also need to save reference to "base" who
// contain parent class, it will be used into this function startup
// to begin at the right chain position.
object._call_base_reference = base;
// Apply super method.
output = descriptor.value.apply(object, args);
// Property have been used into super function if another
// call_base() is launched. Reference is not useful anymore.
delete object._call_base_reference;
// Job is done.
return output;
}
}
// Iterate to the next parent inherited.
base = Object.getPrototypeOf(base);
}
}
ダグラス・クロックフォードのアイデアに基づいたものはどうですか:
function Shape(){}
Shape.prototype.name = 'Shape';
Shape.prototype.toString = function(){
return this.constructor.parent
? this.constructor.parent.toString() + ',' + this.name
: this.name;
};
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.parent = Shape.prototype;
TwoDShape.prototype.name = '2D Shape';
var my = new TwoDShape();
console.log(my.toString()); ===> Shape,2D Shape
子オブジェクトがJavaScriptのプロトタイプチェーンを使用して親のプロパティとメソッドにアクセスできる便利な方法を次に示します。InternetExplorerと互換性があります。 JavaScriptはプロトタイプチェーンでメソッドを検索し、子のプロトタイプチェーンが次のようになるようにします。
子インスタンス->子のプロトタイプ(子メソッドを使用)->親のプロトタイプ(親メソッドを使用)->オブジェクトプロトタイプ-> null
以下の3つのアスタリスク***に示すように、子メソッドはシャドウ化された親メソッドを呼び出すこともできます。
方法は次のとおりです。
//Parent constructor
function ParentConstructor(firstName){
//add parent properties:
this.parentProperty = firstName;
}
//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
console.log(
"Parent says: argument=" + argument +
", parentProperty=" + this.parentProperty +
", childProperty=" + this.childProperty
);
};
ParentConstructor.prototype.commonMethod = function(argument){
console.log("Hello from Parent! argument=" + argument);
};
//Child constructor
function ChildConstructor(firstName, lastName){
//first add parent's properties
ParentConstructor.call(this, firstName);
//now add child's properties:
this.childProperty = lastName;
}
//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;
//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
console.log(
"Child says: argument=" + argument +
", parentProperty=" + this.parentProperty +
", childProperty=" + this.childProperty
);
};
ChildConstructor.prototype.commonMethod = function(argument){
console.log("Hello from Child! argument=" + argument);
// *** call Parent's version of common method
ParentConstructor.prototype.commonMethod(argument);
};
//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');
//call Child method
child_1.childMethod('do child method');
//call Parent method
child_1.parentMethod('do parent method');
//call common method
child_1.commonMethod('do common method');
マルチレベルプロトタイプルックアップにははるかに簡単でコンパクトなソリューションがありますが、Proxy
のサポートが必要です。使用法:SUPER(<instance>).<method>(<args>)
。たとえば、メソッドA
:SUPER(new B).m()
を持つ2つのクラスm
およびB extends A
を想定しています。
function SUPER(instance)
{
return new Proxy(instance,
{
get(target, prop)
{
return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
}
});
}