Reputation: 744
The code example:
function Test() {
this.name = 'test'
}
Test.prototype.normal = function() {
console.log('normal', this.name);
};
Test.prototype.special = {
name: 'special',
start: function () {
console.log("special", this.name);
}
}
test = new Test;
test.normal(); // Response 'normal, test'
test.special.start(); // Response 'special, special'
I'd like the special.start
function to log 'special, test'
How can i achieve this? I am using coffeescript so a coffee example would be appreciated but a javascript example works just as well! Thanks in advance!
Upvotes: 0
Views: 210
Reputation: 21926
To add to the answers already present based on the comments on Kira's answer, you seem to have a fundamental misunderstanding of how scoping works. Javascript has lexical scoping, meaning scope is defined by the structure of the source code, with one exception: this
. this
is dynamically scoped, meaning that scope is defined by the caller. In code:
bar = 3
test = (foo) ->
foo + bar # bar can be seen, because its in an outer scope test can see
So far so good. Now lets define a class:
class Test
constructor: (@foo) ->
getFoo: -> @foo
The getFoo
part is equivalent to Test.prototype.getFoo = function...
. The this
(@) is scoped to getFoo
. The invoker of getFoo
determines the this
:
obj1 = new Test 2
obj1.getFoo() # 2
fn = Test.prototype.getFoo;
fn()
fn
is now a stand alone function instead of a method of obj1
. When we call fn
one of two things will happen. If we're in strict mode it will throw an error. Because we're invoking in a global context this
in getFoo
is undefined. In non-strict it will be the global window
and it will look for a foo property that probably isn't there.
We can force the this
value via bind
, call
, or apply
.
obj2 =
foo: 3
Test.prototype.getFoo.call(obj) # 3
Here we've used it to 'borrow' a method of the Test class. So where does bind come in? Lets say that our class has a method we know will be used in a different context, like an event handler:
class Test
constructor: (@foo) ->
@clickHandler = Test.prototype.clickHandler.bind this
clickHandler: (e) ->
console.log @foo
Because we'll be passing the method to .addEventListener
the this
context will evaporate, meaning we'll need to bind it to each instance, which we do in the constructor.
Upvotes: 0
Reputation: 1443
I think your question is about this argument in a function. Try using apply or call methods
test = function(){ this.name = 'test'; }
test.prototype.special = { name:'special', start : function(){alert(this.name)} }
var t = new test();
t.special.start.call(t);
t.special.start();
In Javascript, if I call a method like obj.myMethod
then this keyword inside myMethod
will refer to the variable obj
. We can change the value of this
keyword inside myMethod
using the functions apply, call and bind.
Instead of using call everywhere, we can create a new function with bind
test = function(){ this.name = 'test'; }
test.prototype.special = { name:'special', start : function(){alert(this.name)} }
var t = new test();
//t.start and t.special.start are now different functions as per MDN
t.start = t.special.start.bind(t);
t.start();
t.special.start();
Upvotes: 1
Reputation: 522025
this
is decided at call time, by how the function is called.test
(the instance), not Test
(the constructor function).If you don't want to make specific call-time adjustments but instead pre-bind the function to the test
instance, that can obviously not happen before you have an instance, and it must happen for each instance individually. Hence the only solution to this is:
function Test() {
this.special = {
start: (function () { ... }).bind(this)
};
}
Perhaps you'll want to define the function on the prototype instead of inline; but you'll still need to bind it in the constructor:
function Test() {
this.special = {
start: this._start.bind(this)
};
}
Test.prototype._start = function () { ... };
Upvotes: 2