27

我想知道一旦构造了函数体,我们还能改变它吗?

     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在这种情况下,如果我们想向它扩展更多功能,我们如何重用。

4

8 回答 8

27

当您使用new时,构造函数内部的值this指向新创建的对象(有关如何new工作的更多信息,请查看此答案此答案)。所以你的新实例i,有一个hello功能。当你试图访问一个对象的属性时,它会沿着原型链向上走,直到找到它。由于hello存在于对象的实例上,因此无需遍历原型链即可访问hello返回的版本hhhhhhhh。从某种意义上说,您已经覆盖了实例中的默认实现。

如果你没有在你的构造函数中赋值hello,你可以看到这个行为:this

var O = function(someValue) {

 }

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

 var i = new O("chris");
 console.log(i.hello()); //this prints out hhhhhhh

你所做的有点倒退。原型基本上提供了某些东西的“默认”形式,您可以在每个实例的基础上覆盖它。仅当在对象上找不到您要查找的属性时才使用默认表单。也就是说,JavaScript 将开始遍历原型链,看看它是否可以找到与您要查找的内容相匹配的属性。它找到它,它将使用它。否则,它将返回undefined

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

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (returns "hello, chris")

因此,当您这样做时i.hello,JavaScript 会看到有一个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不附加到您的任何对象上。

hellothis.hello = function() { .... };如果您的构造函数内部没有,则可以未定义。我还谈到了 JavaScript 用来尝试解析对象属性的一般过程。正如我之前提到的,它涉及到原型链的上游。

于 2012-08-29T17:11:49.237 回答
3

当您使用创建O对象的实例时,new O("somename");您将实例方法分配给新创建的对象。然后,当您将另一个同名的方法分配给O'sprototype时,该方法已经被实例方法隐藏了。所以:

Object.prototype.hello // undefined
       |
       O.prototype.hello // alternate function
         |
         i.hello // original function provided in constructor

JavaScript 从链的底部开始,并在找到 name 的匹配项时停止。所以它停在i.hello并且永远不会看到O.prototype.hello

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

// Possibility #1 - marked as private member
var O = function(someValue) {
    this._someValue = someValue;
};
O.prototype.hello = function() { return "hhhh"; };

var i = new O("somename");
i.hello = function() { return "aloha," + this._someValue; };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #2 - factory function + closure with get and set methods
var OMaker = function(someValue) {
    var realO = function() {};
    realO.prototype.getSomeValue = function() { return someValue; };
    realO.prototype.setSomeValue = function(newVal) { someValue = newVal; };
    realO.prototype.hello = function() { return "hhhh"; };
    return realO;
};
var O = OMaker("somename"),
            i = new O();
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #3 - eschew prototype inheritance and create new objects
var O = function(someValue) {
    return {
        getValue: function() { return someValue; },
        setValue: function(newValue) { someValue = newValue; },
        hello: function() { return "hhhh"; }
    };
};
var i = O(); // Note the lack of the "new" keyword
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

您真的很想阅读bobince 关于 JavaScript 中 OOP 的精彩回答,以获取有关该主题的更多信息。

于 2012-08-29T17:13:08.700 回答
1

不,你不能,但这里有一个很好的例子,可以通过以不同的方式对继承进行模式化,从而绕过该限制。

JavaScript 覆盖方法

// 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"
于 2012-08-29T17:14:39.930 回答
1

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

当您使用构造函数创建对象O时,会发生这种情况:

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

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

其次,你需要了解闭包。

someValueO函数中定义的变量(不是属性)。它只能从同样定义在同一函数中的其他事物(或函数内部定义的任何O函数)访问。所以,我们说“someValue关闭了”。您在O.


为了实现你想要的,你要么需要将 someValue 设置为一个属性(这使它不像一个private东西,而更像一个public东西)。或者,您需要在someValueO原始定义中定义所有需要访问的函数。

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

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

于 2012-08-29T17:22:22.003 回答
0

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

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

于 2012-08-29T17:13:19.017 回答
0

这是因为当您访问对象的属性时,JavaScript 首先检查对象的属性,然后再进入其原型。

这类似于 Java 的派生类覆盖基类功能。

为了更好地理解,请查看继承属性中的代码示例

另请注意,您的情况下的 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
于 2012-08-29T17:17:17.953 回答
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();
于 2012-08-29T17:24:21.040 回答
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);
于 2018-03-30T07:38:49.507 回答