This question already has an answer here:
I'm just getting into using prototypal JavaScript and I'm having trouble figuring out how to preserve a this
reference to the main object from inside a prototype function when the scope changes. Let me illustrate what I mean (I'm using jQuery here):
MyClass = function() {
this.element = $('#element');
this.myValue = 'something';
// some more code
}
MyClass.prototype.myfunc = function() {
// at this point, "this" refers to the instance of MyClass
this.element.click(function() {
// at this point, "this" refers to the DOM element
// but what if I want to access the original "this.myValue"?
});
}
new MyClass();
I know that I can preserve a reference to the main object by doing this at the beginning of myfunc
:
var myThis = this;
and then using myThis.myValue
to access the main object's property. But what happens when I have a whole bunch of prototype functions on MyClass
? Do I have to save the reference to this
at the beginning of each one? Seems like there should be a cleaner way. And what about a situation like this:
MyClass = function() {
this.elements $('.elements');
this.myValue = 'something';
this.elements.each(this.doSomething);
}
MyClass.prototype.doSomething = function() {
// operate on the element
}
new MyClass();
In that case, I can't create a reference to the main object with var myThis = this;
because even the original value of this
within the context of doSomething
is a jQuery
object and not a MyClass
object.
It's been suggested to me to use a global variable to hold the reference to the original this
, but that seems like a really bad idea to me. I don't want to pollute the global namespace and that seems like it would prevent me from instantiating two different MyClass
objects without them interfering with each other.
Any suggestions? Is there a clean way to do what I'm after? Or is my entire design pattern flawed?
For preserving the context, the
bind
method is really useful, it's now part of the recently released ECMAScript 5th Edition Specification, the implementation of this function is simple (only 8 lines long):And you could use it, in your example like this:
Another example:
In this second example we can observe more about the behavior of
bind
.It basically generates a new function, that will be the responsible of calling our function, preserving the function context (
this
value), that is defined as the first argument ofbind
.The rest of the arguments are simply passed to our function.
Note in this example that the function
fx1
, is invoked without any object context (obj.method()
), just as a simple function call, in this type of invokation, thethis
keyword inside will refer to the Global object, it will alert "global test".Now, the
fx2
is the new function that thebind
method generated, it will call our function preserving the context and correctly passing the arguments, it will alert "obj test 1, 2, 3, 4, 5" because we invoked it adding the two additionally arguments, it already had binded the first three.You can set the scope by using the call() and apply() functions
Since you're using jQuery, it's worth noting that
this
is already maintained by jQuery itself:In this example,
o
represents theli
, whereasy
represents the childspan
. And with$.click()
, you can get the scope from theevent
object:Where
e.target
represents theli
, ando
represents the childspan
.For your last
MyClass
example, you could do this:In the function that is passed to
each
,this
refers to a jQuery object, as you already know. If inside that function you get thedoSomething
function frommyThis
, and then call the apply method on that function with the arguments array (see theapply
function and thearguments
variable), thenthis
will be set tomyThis
indoSomething
.I realize this is an old thread, but I have a solution that is much more elegant, and has few drawbacks apart from the fact that it is not generally done, as I have noticed.
Consider the following:
In the function above we defined a local variable (context). We then added a prototypical function (test) that returns the local variable. As you have probably predicted, when we create an instance of this function and then execute the test method, it does not return the local variable because when we defined the prototypical function as a member to our main function, it was outside the scope where the local variable is defined. This is a general problem with creating functions and then adding prototypes to it - you cannot access anything that was created in the scope of the main function.
To create methods that are within the scope of the local variable, we need to directly define them as members of the function and get rid of the prototypical reference:
You may be worried that because the methods are not being created prototypically, different instances may not really be data-separated. To demonstrate that they are, consider this:
Another way to use this method is to create singletons. More often than not, our javascript functions are not being instantiated more than once. If you know that you will never need a second instance of the same function, then there is a shorthand way to create them. Be warned, however: lint will complain that it is a weird construction, and question your use of the keyword 'new':
Pro's: The benefits to using this method to create function objects are plentiful.
Con's: There are some drawbacks to using this method. I don't pretend to be comprehensive :)
Because the methods are defined as members to the object and not prototypes - inheritance can be achieved using member definition but not prototypical definitions.This is actually incorrect. The same prototypical inheritance can be achieved by acting onf.constructor.prototype
.You can create a reference to the this object or you can use the
with (this)
method. The later is extremely useful when your using event handlers and you have no way of passing in a reference.