【问题标题】:Calling method using JavaScript prototype使用 JavaScript 原型调用方法
【发布时间】:2010-10-08 07:57:16
【问题描述】:

如果被覆盖,是否可以从 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(){} 在构造函数中?

标签: javascript prototype overriding


【解决方案1】:

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

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...*/);
}

【讨论】:

  • 我不明白,我来这里的目的和 markvpc 完全一样。我的意思是,我想要一种“私有”方法,它可以从原型中的其他方法中使用。您的回复迫使我创建一个工厂,我想知道是否有任何方法可以避免这种情况。
  • 不要在 JS 中使用“类”字样,因为它会造成混淆。 JavaScript 中没有类
  • 这仅适用于实例化的对象。如果要覆盖所有实例怎么办?
  • 为我工作!正是我需要的。我有 4 个从父级继承的构造函数。其中两个需要覆盖并调用作为其原型的基本构造函数上的方法。这让我能够做到这一点。
  • 我不明白为什么这有这么多赞成票并被标记为正确。这只会覆盖基类的一个实例的行为,而不是子类的所有实例。因此,这并不能回答问题。
【解决方案2】:

一种方法是保存基本方法,然后从覆盖的方法中调用它,就像这样

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

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

        //do something new

    }else{
        return this._do_base();
    }

};

【讨论】:

  • 这将创建无限递归。
  • 我去过那里:无限递归。
  • else 子句调用并运行_do_base,它是对prototype.do 的引用。由于没有参数(或状态)发生变化,代码将再次进入 else,再次调用 _do_base。
  • @JohanTidén _do_base 存储对之前定义的任何函数的引用。如果没有,那就是undefined。 JavaScript 不是make——表达式永远不会像你建议的那样懒惰地求值。现在,如果从 also 继承的原型使用 _do_base 来存储它认为是其基类型的do 实现,那么你确实有问题。所以,是的,如果使用这种方法,闭包将是一种更好的、继承安全的方式来存储对原始函数实现的引用——尽管这需要使用Function.prototype.call(this)
【解决方案3】:

恐怕你的例子不像你想的那样起作用。这部分:

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: The good parts 阅读更多内容。

【讨论】:

  • 确实很好,但你不能真正将base_do 作为一个函数调用,因为这样会丢失原始do 方法中的任何this 绑定。因此,基方法的设置有点复杂,尤其是如果您想使用基对象作为this 而不是子对象来调用它。我会建议类似于base_do.apply(me, arguments)
  • 只是想知道,为什么不使用var 代替base_do?这是故意的,如果是,为什么?而且,正如@GiulioPiancastelli 所说,这会破坏thisbase_do() 内的绑定,还是该调用会从调用者那里继承this
  • @binki 我更新了示例。 var 应该在那里。使用call(或apply)可以让我们正确绑定this
【解决方案4】:

我知道这篇文章是 4 年前的,但由于我的 C# 背景,我一直在寻找一种方法来调用基类,而不必指定类名,而是通过子类上的属性获取它。所以我对Christoph's answer 的唯一更改是

从这里:

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

到这里:

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

【讨论】:

  • 不要这样做,this.constructor 可能并不总是指向MyClass
  • 嗯,它应该,除非有人搞砸了设置继承。 'this' 应该总是引用顶级对象,并且它的 'constructor' 属性应该总是指向它自己的构造函数。
【解决方案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!');

【讨论】:

    【解决方案6】:

    此解决方案使用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());

    【讨论】:

      【解决方案7】:
      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();
          };
      }
      

      【讨论】:

        【解决方案8】:

        另一种选择:

        // 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(...) 路由。
        【解决方案9】:

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

        template method”设计模式可能会帮助您。

        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 */ }
        

        【讨论】:

          【解决方案10】:

          如果你知道你的超类的名字,你可以这样做:

          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
          

          正如预期的那样。

          【讨论】:

            【解决方案11】:

            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()
            

            【讨论】:

              【解决方案12】:

              不,您需要为构造函数中的 do 函数和原型中的 do 函数赋予不同的名称。

              【讨论】:

                【解决方案13】:

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

                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
                

                【讨论】:

                  【解决方案14】:
                  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();
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2016-09-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-10-10
                    • 2012-02-05
                    • 1970-01-01
                    • 2011-02-01
                    相关资源
                    最近更新 更多