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.
精彩评论