开发者

Is it possible to add instance methods to all "classes" in JavaScript?

This is more of an exploratory question, seeing how the core JavaScript stuff works. 开发者_如何学Go I realize the convention is to not override any core JavaScript classes, but I just can't seem to wrap my head around this one.

You can create what acts like "class methods" in JavaScript by adding to the core Function prototype like this:

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

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

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

My question is, is there a way to add "instance methods" in a similar way? Something crazy like this, but what's below doesn't work (or make any sense):

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

It's almost like you'd need to override the Function class' constructor method and access the prototype from there. Is this possible?

It does work if you add to the Object.prototype like this:

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

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

But that doesn't seem right either, mainly because seeing the output in the node.js console from console.log({}); change is confusing:

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

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

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


If you are using node.js, you should be able to use Object.defineProperty [MDN] and make the new property non-enumerable:

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

This was introduced in ECMAScript5, so only newer browsers will support it.


It's important to understand when the prototype comes into play. It's simply an object that is a property of a function. It only has meaning when you use the new keyword. Example:

    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

When new is used, the js interpreter will create a hidden _proto_ property on the instance. This proto link is simply a reference to the prototype object of the constructor function, e.g., Widget at the time the constructor was called.

When you override the Function constructor, you are literally adding something that will be on the _proto_ property of every function created after you modified Function.prototype.

If you make the statement child.prototype... = ... in your base 'class' constructor function, then that prototype will not have meaning until something 'instantiates' child, e.g., var child = new child();.

A great Resource.

To answer your question about 'instance methods', you simply need to do something like the following:

    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

This is due to javascript's implementation of Prototypical Inheritance. The proto link I spoke of before is key here. When widget1 was first created, inside the constructor function Widget, method was attached specifically to widget1. This method will not be available to other instances. However, method on the prototype is shared across all instances of Widget.

At runtime, when the js interpreter sees widget1.method();, it first sees if widget1 has method as a property directly on it (objects in js are just hashmaps in essence, in which the keys are called 'properties'). It finds the instance method as a property in this case. However, once you delete the instance method, it will attempt to follow the _proto_ link, which is just an object reference to Widget.prototype (at the time the constructor was called). Widget.prototype.method is defined; therefore, the interpreter will execute that. If no method function is found when continuing to follow _proto_ links, it'll be a run-time error.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜