JavaScript中的类/实例方法重写

Class/instance method overrides in JavaScript

本文关键字:实例方法 重写 JavaScript      更新时间:2023-09-26

在JS中使用相同的名称定义类(静态)和原型方法是否普遍可以接受和合理?在下面的node.js示例中,方法update以这样的方式定义,并且示例工作得很好,但是是否有任何常见的情况下,这可能会导致问题,例如,在实例上调用update会分派给类方法?

function Base(id, value) {
    this.id = id;
    this.value = value;
    Base.objects[id] = this;
}
Base.objects = {};
Base.find = function(id, done) {
    var err = null;
    var obj = this.objects[id];
    if (!obj) {
        err = new Error("no obj found for id " + id);
    }
    done(err, obj);
};
Base.update = function(id, value, done) {
    this.find(id, function(err, obj) {
        if (err) {
            done(err);
        } else {
            obj.update(value, done);
        }
    });
};
Base.prototype.update = function(value, done) {
    this.value = value;
    done();
};

试用:

new Base('aa', 1);
new Base('bb', 2);
Base.update('aa', 3, function(err) {
    if (err) {
        throw err;
    }
});
Base.update('bb', 5, function(err) {
    if (err) {
        throw err;
    }
});
console.log(Base.objects);
输出:

{ aa: { id: 'aa', value: 3 }, bb: { id: 'bb', value: 5 } }

我不太确定你在问什么,但我想你想知道你是否要打电话:

(new Base('aa', 1)).update(3, function (err){ if (err) throw err;});

可以调用Base.update函数。

这个问题的答案当然是:No

Base.update函数与Base.prototype.update无关。函数,就像JS中的几乎所有东西一样,是一个对象。就像您使用new Base创建的Base实例一样。Base函数(恰好兼作构造函数)有自己的继承链:

Base //the object itself
  || Object.getPrototypeOf(Base)
  ====> Function (or function Empty(){} in chromium)
         || Object.getPrototypeOf(Object.getPrototypeOf(Base));
         ====> Object {}

当您创建一个新的Base对象时,该链看起来像这样:

x // Base instance itself
 ||
  --> Base.prototype //or Base {} <-- an object!
       ||
        --> Object{}

这意味着任何Base实例的任何属性都必须位于该继承链上:要么定义在对象本身上,要么定义在Base.prototype对象上,要么定义在所有对象的母体:Object上。

在任何情况下都不会考虑函数对象 Base

那么,这到底是什么意思呢?

var x = new Base('x', 1);
console.log(x.update === Base.prototype.update);//true
console.log(x.update === Base.update);//false, of course

但是如果你要写:

x.update = Base.update;

然后重写原型,在x上调用update(这是Base的一个实例),然后Base.update将被调用。然而,它不会像预期的那样工作,因为它的调用上下文已经改变。this现在引用x和下面的语句:

 this.find(...)

将不再工作,除非你也添加x.find = Base.find。但是,现在我们在x.find方法中遇到了类似的问题:

var obj = this.objects[id];

x没有objects属性…要解决这个问题,您必须添加x.objects = Base.objects以使其全部工作。我们最后得到:

x = new Base('x', 1);
x.update = Base.update;
x.find = Base.find;
x.objects = Base.objects;

但是现在我们已经将Base函数对象的所有属性赋值给Base实例x。这太愚蠢了,所以让我们删除它们:

delete(x.update);
delete(x.find);
delete(x.objects);

现在,x.update将再次像预期的那样工作:实例本身不会掩盖原型方法,因此将调用原型方法。

这只是JS如何计算表达式以及如何扫描原型(和作用域)的快速浏览。我之前已经详细解释过了。这个链接的答案还包含更多的链接,每个链接的答案在某种程度上都与相同的事情相关(范围和原型链)。

JS是基于原型的语言。在这种语言中,从某个对象调用方法就是调用存储在该对象本身中具有该名称或在其__proto__中的某个级别的函数。

您感兴趣的不匹配只可能在SomeObj.prototype==SomeObj。如果你不做这样的事情,这将不会出现在任何时候。