Reputation: 2518
JavaScript defines Object.prototype
as a way to define methods or properties for class-like objects.
A big problem that exists when using Object.prototype
is the incorrect behavior when a method is invoked via a callback as this
will have the invoking context.
function Foo() {}
Foo.prototype.resize() {
console.log(this);
}
var foo = new Foo();
window.on('resize', foo.resize);
So to work around this limitation we can do the following.
function Foo2() {
function resize() {
console.log(self);
}
var self = this;
this.resize = resize;
}
var foo2 = new Foo2();
window.on('resize', foo2.resize);
Given the above situation, it seems like it's better to always define class-like objects in example 2.
Thus, when should we use the Object.prototype
facilities? I mean, you cannot know a priori how your methods will be called, and most certainly you would want the correct this
context reference when invoked.
Supporting context: I thought of this question as it seems like you are creating instance methods using Object.prototype
. However, as illustrated, you are not. You are simply creating methods that have no "real" connection to their instance. Hence the need to use closure to create an instance-bound method. Furthermore, this question was thought of after reading this excellent explanation of this
.
Upvotes: 3
Views: 156
Reputation: 105489
Your confusion comes from the fact that you view JavaScript objects as having methods like in OOP languages. And so you believe they should have fixed context, since methods in OOP languages usually use "early binding" and so they are bound to the correct context. It's different in JavaScript. It's sort of "late binding", when the context of the function - this
- is determined when the function is executed. To me, it's beneficial to see methods as simply object properties pointing to a function, that indeed can be executed in different contexts. For example, we could have something like this:
function resize() {
console.log(this);
}
resize.call(window);
resize.call(custom);
If you want, certainly you can achieve "early binding" using bind
for example:
function O() {
this.resize = function() {}.bind(this);
}
var o = new O();
But that limits re-usability of the object methods. For example, this wouldn't be possible:
Array.prototype.slice.call(arguments)
You can read here for some suggestions on why methods are not bound even in ES6.
Prototypes are not static methods
, they are created to enable memory efficient code reuse. As JLRishe
pointed out, the biggest advantage of prototypes is memory usage reduction, since you can define one instance of a function on prototype, and have convenient access to it as object property that have the prototype in their prototype chain. But the prototype is just for convenience. Here is the example with resize
without prototype
:
// here only one instance of `resize` function is created
function resize() {
console.log(this);
}
var o1 = {
resize: resize
}
var o2 = {
resize: resize
}
You are simply creating methods that have no "real" connection to their instance.
Correct, here is the example of a prototype with "methods" later to be used with different contexts:
var prototype = {
resize: function() {
console.log(this);
}
}
var o1 = {
resize: resize
}
Object.setPrototypeOf(o1, prototype);
var o2 = {
resize: resize
}
Object.setPrototypeOf(o2, prototype);
I think JavaScript was built with idea in mind that functions are first-class objects, not that objects should have methods with correctly bound context.
Static methods are usually implemented as properties on function constructors, like this:
function SomeObjectConstructor() {}
SomeObjectConstructor.someStaticMethod = function() {}
Upvotes: 3
Reputation: 1558
I think that directly binding an object's method to an event handler, while possible, is a shortcut you want to avoid.
window.on('resize', function () {
foo.resize();
});
Even though it's more verbose, I think writing your handlers like this is clearer and wont affect the context of this
in your Object.prototype
methods.
As stated in one of the comments, using the object's prototype is more efficient than defining methods for each instance of your object.
Upvotes: 1