Javascript重新定义并覆盖现有的函数体

时间:2012-08-29 17:02:36

标签: javascript prototype

我想知道一旦它构建完成后我们还能改变它吗?

     var O = function(someValue){
           this.hello = function(){
                return "hello, " + someValue;
           }
     }

     O.prototype.hello = function(){
           return "hhhhhhh";
     }

     var i = new O("chris");
     i.hello();   // -> this still returns the old definition "hello, chris"

javascript语句O.prototype.hello = function(){....}不会覆盖并重新定义hello函数行为。这是为什么 ?我知道如果您尝试重用参数someValue将会出现类型错误。

      // this will fail since it can't find the parameter 'someValue'
      O.prototype.hello = function(){
             return "aloha, " + someValue;
      } 

我想知道为什么它允许在运行时添加函数,如

      O.prototype.newFunction = function(){
           return "this is a new function";
      }

      i.newFunction();   //  print 'this is a new function' with no problem.

但不允许您在定义后更改定义。 我做错什么了吗 ?我们如何覆盖和重新定义类中的函数?有没有办法重用我们之前传递的参数来创建对象?在这种情况下,如果我们想要向其扩展更多功能,我们如何重新使用someValue

8 个答案:

答案 0 :(得分:23)

当您使用new时,构造函数中this的值指向新创建的对象(有关new如何工作的详细信息,请查看this answer }和this answer)。因此,您的新实例i具有hello功能。当您尝试访问对象的属性时,它会沿着原型链向上走,直到找到它为止。由于hello存在于对象的实例上,因此无需走原型链来访问返回hello的{​​{1}}版本。从某种意义上说,您已经覆盖了实例中的默认实现。

如果您未在构造函数中将hhhhhhhh分配给hello,则可以看到此行为:

this

你正在做的事情是倒退。原型基本上提供了某种东西的“默认”形式,您可以在每个实例的基础上覆盖它。仅当在对象上找不到您要查找的属性时,才使用默认表单。也就是说,JavaScript将开始走向原型链,看看它是否能找到与您正在寻找的匹配的属性。它找到它,它将使用它。否则,它将返回var O = function(someValue) { } O.prototype.hello = function(){ return "hhhhhhh"; } var i = new O("chris"); console.log(i.hello()); //this prints out hhhhhhh

在第一种情况下你基本上拥有的内容如下:

undefined

因此,当您执行Object.prototype.hello (not defined; returns "undefined") | +----O.prototype.hello (returns "hhhhhhhh") | +----i.hello (returns "hello, chris") 时,JavaScript会在i.hello上看到hello属性并使用该属性。现在,如果您没有明确定义i属性,则基本上具有以下内容:

hello

这意味着您可以在原型中提供默认实现,然后覆盖它(在某种意义上它就像子类)。您还可以做的是通过直接修改实例来修改每个实例的行为。你在原型上拥有的Object.prototype.hello (not defined; returns "undefined") | +----O.prototype.hello (returns "hhhhhhhh") | +----i.hello (is "undefined", so JavaScript will walk up the chain until it sees O.prototype.hello, which does have a defined value it can use.) 版本是一种故障安全和后备。

编辑:您的问题的答案:

基于每个实例的覆盖意味着您将属性或函数附加到特定实例。例如,你可以这样做:

hello

这意味着此行为特定于特定实例(即仅限于i.goodbye = function() { return "Goodbye, cruel world!"; }; ,而不是您可能已创建的任何其他实例。

如果你取出i,那么你基本上有:

this

这等同于:

hello = function() {
    return "hello, " + someValue;
}

因此,在这种情况下,window.hello = function() { return "hello, " + someValue; } 是对该函数的全局引用。这意味着hello没有附加到任何对象上。

如果构造函数中没有hello,则

hello可能未定义。我还谈到了JavaScript用来尝试解析对象属性的一般过程。正如我之前提到的,它涉及走原型链。

答案 1 :(得分:3)

使用O创建new O("somename");对象的实例时,您正在为新创建的对象分配实例方法。然后,当您为O的{​​{1}}分配另一个同名方法时,该方法已被实例方法隐藏。所以:

prototype

JavaScript从链的底部开始,在找到名称匹配时停止。因此,它会停在Object.prototype.hello // undefined | O.prototype.hello // alternate function | i.hello // original function provided in constructor ,永远不会看到i.hello

JavaScript(从ECMAScript 5开始)实际上没有(据我所知)给你一个很好的方法来做私有变量,可以通过定义后添加的实例方法访问(添加 on 实例或O.prototype.hello)。闭包可以帮助您完成大部分工作,但是如果您希望能够在闭包之外添加可以访问闭包变量的方法,则需要公开prototype和/或get方法来提供这些新方法。方法访问闭包变量:

set

您真的想阅读bobince's great answer on OOP in JavaScript了解有关此主题的更多信息。

答案 2 :(得分:1)

没有。你不能,但是这里有一个很好的例子,可以通过以不同的方式模式化你的继承来解决这个限制。

JavaScript override methods

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

答案 3 :(得分:1)

首先,您需要了解原型继承。

使用O作为构造函数创建对象时,会发生以下情况:

  • 1,创建一个新对象。
  • 第二,该对象的hello属性设置为一个函数(通过您定义的构造函数)。
  • 3,创建指向O.prototype对象的对象的秘密链接。

引用O个对象的属性时,首先在对象本身中查找这些属性。只有当对象本身没有属性时,它才会查看它的原型。

其次,您需要了解闭包。

someValueO函数中定义的变量(不是属性)。它只能从同一函数中定义的其他东西(或O函数中定义的任何函数)访问。因此,我们说“someValue 关闭”。您在O之外定义的函数无法访问它。


要实现您想要的功能,您需要将someValue设置为属性(这使得它不像private那样,更像是public的东西。或者,您需要在someValue的原始定义中定义需要访问O的所有函数。

要更改i.hello创建i之后的内容,您需要直接设置对象的属性。

i.hello = function () { /* stuff */ };

答案 4 :(得分:0)

如果我没记错的话,作为对象直接成员的函数优先于该对象原型的同名成员。因此,O.prototype.helloO.hello篡夺,即使前者在代码中稍后定义。

someValue无法使用O.prototype.hello的原因是因为someValue的范围受限于构造函数以及在其中定义或执行的任何函数。由于O.prototype.hello的定义超出了O构造函数的范围,因此它不了解someValue

答案 5 :(得分:0)

这是因为当您访问Object的属性时,JavaScript会在进入其原型之前首先检查对象的属性。

这类似于Java的派生类重写基类功能。

为了更好地理解,请查看Inheriting properties

中的代码示例

另请注意,在您的情况下someValue是构造函数的本地。 如果在其他函数中需要它,则应将其分配给构造函数中的this.someValue。

您可以在此处覆盖特定对象的hello函数,如下所示。但不是整个班级。

i.hello = function(){ console.log('even here someValue is not accessible');};

  var O = function(someValue){
       this.someValue = someValue;
       this.hello = function(){
            return "hello, " + someValue;
       }
 }

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 i.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // this still prints hello, Sujay

请注意,这里我们没有更改构造函数,因此在上面的示例中,这不适用于其他实例,例如test

最好的方法是仅在原型和放大器中定义功能。不在构造函数中,如下面的代码片段。

 var O = function(someValue){
      this.someValue = someValue;
 };
 O.prototype.hello = function(){
            return "hello, " + this.someValue;
 };

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 O.prototype.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // prints Hi there Sujay

答案 6 :(得分:0)

当您访问属性时,系统首先在实例中查找它。如果找不到,它会在原型中查找它。这就是使用this.hello的原因,而不是O.prototype.hello。

如果您希望覆盖hello的实现,则需要使用JavaScript继承。这是一个基本的例子:

var A = function(){
    console.log("A is getting constructed");
};

A.prototype.constructor = A;
A.prototype.someValue = 1;
A.prototype.hello = function() {
    console.log("A.hello(): " + this.someValue);
};

var B = function(){
    //Constructor of A is automatically called before B's

    console.log("B is getting constructed");
};
B.prototype = new A; //Inherit from A
B.prototype.constructor = B;
B.prototype.hello = function() {
    console.log("B.hello() called");
    console.log("Calling base class method");
    A.prototype.hello.call(this);
};

var a = new A();
a.hello();

var b = new B();
b.hello();

答案 7 :(得分:0)

为已创建的实例覆盖方法 refreshEditor

var cp = hot1.getPlugin('comments');
cp.refreshEditor = (function (original) {
  return function (force) {
    //do something additional
    if(console) {
      console.log('test!!!!!!!!!');
      console.log(force)
    }
    original.call(cp, force);
  }
})(cp.refreshEditor);