[英]How to call a parent method from child class in javascript?
在过去的几个小时里,我一直在试图找到解决问题的方法,但这似乎是无望的。
基本上我需要知道如何从子类调用父方法。 到目前为止,我尝试过的所有东西都以无法正常工作或覆盖父方法而告终。
我正在使用以下代码在 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 中完成的。
非常感谢任何帮助,谢谢!
ES6 风格允许你使用新的特性,比如super
关键字。 当您使用 ES6 类语法时, super
关键字都是关于父类上下文的。 作为一个非常简单的例子,结帐:
请记住:我们不能通过实例方法中的
super
关键字调用父静态方法。 调用方法也应该是静态的。
通过实例方法调用静态方法 - TypeError !
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo { classMethod() { return super.classMethod() + ', too'; } } console.log(Bar.classMethod()); // 'hello' - Invokes inherited static method console.log((new Bar()).classMethod()); // 'Uncaught TypeError' - Invokes on instance method
通过super
调用静态方法 - 这行得通!
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo { static classMethod() { return super.classMethod() + ', too'; } } console.log(Bar.classMethod()); // 'hello, too'
现在super
上下文会根据调用发生变化——瞧!
class Foo { static classMethod() { return 'hello i am static only'; } classMethod() { return 'hello there i am an instance '; } } class Bar extends Foo { classMethod() { return super.classMethod() + ', too'; } } console.log((new Bar()).classMethod()); // "hello there i am an instance , too" console.log(Bar.classMethod()); // "hello i am static only"
此外,您可以使用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 并快乐。
这是它的完成方式: ParentClass.prototype.myMethod();
或者如果你想在当前实例的上下文中调用它,你可以这样做: ParentClass.prototype.myMethod.call(this)
从带有参数的子类调用父方法也是如此: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)
*提示:使用apply()
而不是call()
将参数作为数组传递。
好吧,为了做到这一点,您不受 ES6 的Class
抽象的限制。 可以通过__proto__
属性访问父构造函数的原型方法(我很确定会有其他 JS 编码人员抱怨它已被贬值),该属性已被贬值但同时发现它实际上是子类化需求的重要工具(特别是对于 Array 子类化需求)。 因此,尽管__proto__
属性在我所知道的所有主要 JS 引擎中仍然可用,但 ES6 在其之上引入了Object.getPrototypeOf()
功能。 Class
抽象中的super()
工具是 this 的语法糖。
因此,如果您无权访问父构造函数的名称并且不想使用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);
}
}
这是子对象使用 JavaScript 的原型链访问父属性和方法的好方法,它与 Internet Explorer 兼容。 JavaScript 在原型链中搜索方法,我们希望孩子的原型链看起来像这样:
子实例 -> 子原型(带有子方法)-> 父原型(带有父方法)-> 对象原型 -> null
子方法也可以调用隐藏的父方法,如下面的三个星号 *** 所示。
就是这样:
//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');
在多继承级别的情况下,该函数可以用作其他语言中的 super() 方法。 这是一个演示小提琴,有一些测试,你可以像这样使用它,在你的方法中使用: 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);
}
}
基于 Douglas Crockford 想法的东西怎么样:
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
多级原型查找有一个更简单、更紧凑的解决方案,但它需要Proxy
支持。 用法: SUPER(<instance>).<method>(<args>)
,例如,假设两个类A
和B extends A
使用方法m
扩展 A: SUPER(new B).m()
。
function SUPER(instance) {
return new Proxy(instance, {
get(target, prop) {
return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
}
});
}
使用经典 js 更灵活的答案。
你定义“_parent = A.prototype;” 在子类中,您可以使用 apply 调用父类的方法:
class A{ _msg='A'; _msgOnlyA=' great from A'; constructor(){ } hello(){ console.log('hello '+this._msg+', '+this._msgOnlyA); } }; class B extends A{ _parent = A.prototype; _msg='B'; constructor(){ super(); } hello(){ this._parent.hello.apply(this, arguments); console.log('hello '+this._msg); } }; var b = new B(); b.hello();
虽然您可以通过父的原型调用父方法,但您需要传递当前的子实例才能使用call
、 apply
或bind
方法。 bind
方法将创建一个新函数,因此如果您关心性能,我不建议您这样做,除非它只调用一次。
作为替代方案,您可以替换子方法并将父方法放在实例上,同时调用原始子方法。
function proxy(context, parent){ var proto = parent.prototype; var list = Object.getOwnPropertyNames(proto); for(var i=0; i < list.length; i++){ var key = list[i]; // Create only when child have similar method name if(context[key] !== proto[key]){ let currentMethod = context[key]; let parentMethod = proto[key]; context[key] = function(){ context.super = parentMethod; return currentMethod.apply(context, arguments); } } } } // ========= The usage would be like this ========== class Parent { first = "Home"; constructor(){ console.log('Parent created'); } add(arg){ return this.first + ", Parent "+arg; } } class Child extends Parent{ constructor(b){ super(); proxy(this, Parent); console.log('Child created'); } // Comment this to call method from parent only add(arg){ return super.add(arg) + ", Child "+arg; } } var family = new Child(); console.log(family.add('B'));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.