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

Javascript redefine and override existing function body

本文关键字:覆盖 函数体 新定义 定义 Javascript      更新时间:2023-09-26

我想知道,一旦构建好函数体,我们还能更改它吗?

     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扩展更多的函数,我们如何重用它。

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

如果不在构造函数中将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会看到i上有一个hello属性并使用它。现在,如果您没有明确定义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没有附加到任何对象。

如果您的构造函数中没有this.hello = function() { .... };,那么hello可能是未定义的。我还谈到了JavaScript用来解析对象属性的一般过程。正如我之前提到的,它涉及到走上原型链。

当您使用new O("somename");创建O对象的实例时,您正在将实例方法分配给新创建的对象。然后,当您将另一个同名方法分配给Oprototype时,该方法已被实例方法覆盖。因此:

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

JavaScript从链的底部开始,当找到与名称匹配的名称时停止。因此,它在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的精彩回答,了解更多关于这个主题的信息。

编号。你不能,但这里有一个很好的例子,通过以不同的方式模式化你的继承,可以绕过这个限制。

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"

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

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

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

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

其次,您需要了解闭包

someValue是在O函数中定义的变量(而不是属性)。它只能从同一函数(或O函数内部定义的任何函数)中定义的其他事物访问。因此,我们说"someValue闭合的"。您在O之外定义的函数无法访问它。


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

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

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

如果我没有记错,作为对象的直接成员的函数优先于该对象原型的类似命名成员。因此,O.prototype.helloO.hello取代,即使前者在代码的后面定义。

someValueO.prototype.hello不可用的原因是,someValue的作用域被约束为构造函数以及在其中定义或执行的任何函数。由于O.prototype.hello是在O构造函数的作用域之外定义的,所以它不知道someValue

当您访问属性时,系统首先在实例中查找它。如果没有找到,它会在原型中查找它。这就是为什么使用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();

这是因为当您访问对象的属性时,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

覆盖已创建实例的方法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);