是否可以在JavaScript中为所有“类”添加实例方法?

时间:2011-10-13 06:16:16

标签: javascript

这是一个探索性的问题,看看核心JavaScript的工作原理。我意识到惯例是不覆盖任何核心JavaScript类,但我似乎无法绕过这个类。

您可以通过添加到核心Function原型来创建JavaScript中的“类方法”,如下所示:

Function.prototype.class_method = function() {
  console.log("class method called")
}

var User;
User = (function() {
  function User() {}
  return User;
})();

User.class_method(); // "class method called"

我的问题是,有没有办法以类似的方式添加“实例方法”?像这样疯狂的东西,但是下面的东西不起作用(或者说没有任何意义):

alias = Function.prototype.constructor;
Function.prototype.constructor = function() {
  child = this;
  child.prototype.instance_method = function() {
    console.log("instance method called");
  }
  alias.apply(child);
}

var user = new User();
user.instance_method(); // method doesn't exist

这几乎就像你需要覆盖Function类'constructor方法并从那里访问prototype。这可能吗?

如果您像这样添加Object.prototype,它确实有效:

Object.prototype.instance_method = function() {
  console.log("instance method");
}

var user = new User();
user.instance_method(); // "instance method called"

但这似乎也不对,主要是因为看到来自console.log({});的node.js控制台中的输出变化令人困惑:

console.log({});
// {};

Object.prototype.instance_method = function() {
  console.log("instance method");
}

console.log({});
// {"instance_method": [Function]}

2 个答案:

答案 0 :(得分:2)

了解原型何时发挥作用非常重要。它只是一个对象,是一个函数的属性。它只在您使用new关键字时才有意义。例如:

    var Widget = function(val) {
        this.value = val;
    };
    Widget.prototype.getValue = function() {
        return this.value;
    };
    var widget1 = new Widget('test');
    widget1.getValue();  // test
    var widget2 = new Widget('test2');
    widget2.getValue();  // test2

当使用new时,js解释器将在实例上创建隐藏的_proto_属性。这个proto链接只是对构造函数的原型对象的引用,例如,在调用构造函数时的Widget。

当您覆盖Function构造函数时,您实际上会添加一些内容,这些内容将在您修改_proto_后创建的每个函数的Function.prototype属性上。

如果你在你的基类'class'构造函数中创建了child.prototype... = ...语句,那么该原型在'实例化'子项之前就没有意义,例如var child = new child();

很棒的Resource

要回答有关“实例方法”的问题,您只需执行以下操作:

    var Widget = function() {
        this.method = function() {
            return 'instance method';
        };
    };
    Widget.prototype.method = function() {
        return 'class method';
    };
    var widget1 = new Widget();
    widget1.method();  // instance method
    delete widget1.method;
    widget1.method();  // class method

这是由于javascript实现了Prototypical Inheritance。我之前谈到的 proto 链接是关键所在。首次创建widget1时,在构造函数Widget中,method专门附加到widget1。此method将无法用于其他实例。但是,原型上的method在Widget的所有实例中共享。

在运行时,当js解释器看到widget1.method();时,它首先看到widget1是否有method作为其上的属性(js中的对象本质上只是哈希映射,其中这些键被称为“属性”)。在这种情况下,它将实例方法作为属性查找。但是,一旦删除实例方法,它将尝试遵循_proto_链接,这只是对Widget.prototype的对象引用(在构造函数被调用时)。 Widget.prototype.method已定义;因此,解释器将执行该操作。如果在继续关注method链接时未找到_proto_功能,则会出现运行时错误。

答案 1 :(得分:2)

如果您使用的是node.js,则应该能够使用Object.defineProperty [MDN]并使新属性不可枚举

Object.defineProperty(Object.prototype, 'instance_Method', {
    value: function() {
        console.log("instance method");
    },
    enumerable: false // it's already the default
});

这是在ECMAScript5中引入的,因此只有较新的浏览器才会支持它。