使用JavaScript原型的调用方法


129

如果重写了JavaScript中的原型方法,则可以调用基础方法吗?

MyClass = function(name){
    this.name = name;
    this.do = function() {
        //do somthing 
    }
};

MyClass.prototype.do = function() {  
    if (this.name === 'something') {
        //do something new
    } else {
        //CALL BASE METHOD
    }
};

哪种基本方法?this.do = function(){}在构造函数中?
Ionuț G. Stan,

Answers:


200

我不明白您到底想做什么,但通常按照以下方式完成特定于对象的行为:

function MyClass(name) {
    this.name = name;
}

MyClass.prototype.doStuff = function() {
    // generic behaviour
}

var myObj = new MyClass('foo');

var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
    // do specialised stuff
    // how to call the generic implementation:
    MyClass.prototype.doStuff.call(this /*, args...*/);
}

1
我不明白这一点,我来这里的确切目的与markvpc相同。我的意思是,我希望有一种“私有”方法,该方法可用于原型中的其他方法。您的回应迫使我创建了一家工厂,我想知道是否有任何方法可以避免这种情况。
R01010010 2014年

10
请勿在JS中使用“类”词,因为这会造成混淆。JavaScript中没有类
Polaris 2015年

1
这仅适用于实例化的对象。如果要覆盖所有实例怎么办?
supertonsky 2015年

为我工作!正是我所需要的。我有4个从父代继承的构造函数。其中两个需要重写并在作为其原型的基本构造函数上调用一个方法。这使我能够做到这一点。
binarygiant

3
我不明白为什么会有这么多的投票并被标记为正确。这仅覆盖基类的一个实例的行为,而不是子类的所有实例的行为。因此,这不能回答问题。
BlueRaja-Danny Pflughoeft 2015年

24

很好的一种方法是保存基本方法,然后从重写方法中调用它,如下所示

MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){  

    if (this.name === 'something'){

        //do something new

    }else{
        return this._do_base();
    }

};

11
这将创建无限递归。
约翰·蒂登(JohanTidén)2012年

3
我去过那里:无限递归。
jperelli

else子句正在调用并运行_do_base,这是对prototype.do的引用。由于未更改任何参数(或状态),因此代码将再次进入else,再次调用_do_base。
约翰·提登(JohanTidén)

10
@JohanTidén _do_base存储了对之前定义的任何函数的引用。如果没有,那就应该是undefined。JavaScript不是— make表达式永远不会像您建议的那样懒惰地求值。现在,如果继承自原型的原型使用_do_base存储它认为是其基本类型的实现do,那么您确实有问题。因此,是的,如果使用此方法,闭包将是一种更好的,继承安全的方式来存储对原始函数实现的引用,尽管这需要使用Function.prototype.call(this)
2015年

16

恐怕您的榜样不符合您的想法。这部分:

this.do = function(){ /*do something*/ };

覆盖的定义

MyClass.prototype.do = function(){ /*do something else*/ };

由于新创建的对象已经具有“ do”属性,因此它不会查找原型链。

Javascript中的经典继承形式很笨拙,而且很难掌握。我建议改用Douglas Crockfords简单的继承模式。像这样:

function my_class(name) {
    return {
        name: name,
        do: function () { /* do something */ }
    };
}

function my_child(name) {
    var me = my_class(name);
    var base_do = me.do;
    me.do = function () {
        if (this.name === 'something'){
            //do something new
        } else {
            base_do.call(me);
        }
    }
    return me;
}

var o = my_child("something");
o.do(); // does something new

var u = my_child("something else");
u.do(); // uses base function

我认为,使用javascript处理对象,构造函数和继承的方法更加清晰。您可以在Crockfords Javascript:优秀部分中阅读更多内容。


3
确实很好,但是您不能真正base_do作为函数调用,因为那样会丢失this原始do方法中的任何绑定。因此,基本方法的设置要复杂一些,特别是如果您要使用基本对象this而不是子对象来调用它。我建议类似的东西base_do.apply(me, arguments)
Giulio Piancastelli,2014年

只是想知道,你为什么不使用varbase_do?这是故意的吗?如果是,为什么?而且,正如@GiulioPiancastelli所说,此中断this绑定在内部base_do()还是该调用this将从其调用方继承?
2015年

@binki我更新了示例。本var应该在那里。使用call(或apply)可以使我们this正确绑定。
Magnar

10

我知道这篇文章是4年前写的,但是由于我的C#背景,我一直在寻找一种无需指定类名而是通过子类上的属性来调用基类的方法。因此,我对克里斯多夫答案的唯一改变是

由此:

MyClass.prototype.doStuff.call(this /*, args...*/);

对此:

this.constructor.prototype.doStuff.call(this /*, args...*/);

6
不要这样做,this.constructor可能并不总是指向MyClass
Bergi 2014年

好吧,应该这样,除非有人搞砸了设置继承。“ this”应始终引用顶级对象,并且其“ constructor”属性应始终指向其自己的构造函数。
Triynko '17

5

如果您定义这样的功能(使用OOP)

function Person(){};
Person.prototype.say = function(message){
   console.log(message);
}

调用原型函数有两种方法:1)创建实例并调用对象函数:

var person = new Person();
person.say('hello!');

而另一种方法是... 2)直接从原型中调用函数:

Person.prototype.say('hello there!');

5

该解决方案使用 Object.getPrototypeOf

TestA 超级有 getName

TestB是一个孩子重写getName,但也有 getBothNames一个调用super的版本getName,以及该child版本

function TestA() {
  this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
  this.count = 2;
  return ' TestA.prototype.getName is called  **';
};

function TestB() {
  this.idx = 30;
  this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
  return ' TestB.prototype.getName is called ** ';
};

TestB.prototype.getBothNames = function tb_gbn() {
  return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};

var tb = new TestB();
console.log(tb.getBothNames());


4
function NewClass() {
    var self = this;
    BaseClass.call(self);          // Set base class

    var baseModify = self.modify;  // Get base function
    self.modify = function () {
        // Override code here
        baseModify();
    };
}

4

替代 :

// shape 
var shape = function(type){
    this.type = type;
}   
shape.prototype.display = function(){
    console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){ 
    // call implementation of the super class
    this.__proto__.display.apply(this,arguments);
}

尽管可能可行,但我不建议您使用此解决方案。有很多原因__proto__应避免使用(更改对象的[[Prototype]]`)。请改用Object.create(...)路线。
卡雷尔格'16

2

如果我理解正确,则希望始终执行基本功能,而其中的一部分应留给实现。

模板方法 ”设计模式可能会为您提供帮助。

Base = function() {}
Base.prototype.do = function() { 
    // .. prologue code
    this.impldo(); 
    // epilogue code 
}
// note: no impldo implementation for Base!

derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }

1

如果您通过名称知道您的超类,则可以执行以下操作:

function Base() {
}

Base.prototype.foo = function() {
  console.log('called foo in Base');
}

function Sub() {
}

Sub.prototype = new Base();

Sub.prototype.foo = function() {
  console.log('called foo in Sub');
  Base.prototype.foo.call(this);
}

var base = new Base();
base.foo();

var sub = new Sub();
sub.foo();

这将打印

called foo in Base
called foo in Sub
called foo in Base

如预期的那样。


1

ES5的另一种方法是使用以下命令明确遍历原型链 Object.getPrototypeOf(this)

const speaker = {
  speak: () => console.log('the speaker has spoken')
}

const announcingSpeaker = Object.create(speaker, {
  speak: {
    value: function() {
      console.log('Attention please!')
      Object.getPrototypeOf(this).speak()
    }
  }
})

announcingSpeaker.speak()


0

另外,如果您想覆盖所有实例,而不仅仅是一个特殊实例,那么这个实例可能会有所帮助。

function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

结果:

doing original
doing override

-1
function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.