2

i am getting undefined undefined when i defined and call the setTimeout function as follow:

var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        console.log(this.first + ' ' + this.last);
    }
}

setTimeout(person.getName(), 2000);

unless if i wrap the person.getName() in a function as follow:

setTimeout(function(){
    person.getName()
}, 2000);

Now i am getting a right output. Why it's so?

d3bug3r
  • 2,042
  • 3
  • 29
  • 64

6 Answers6

3

Assuming you meant:

var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        console.log(this.first + ' ' + this.last);
    }
}

setTimeout(person.getName, 2000);

The reason you get undefined is that what you are effectively doing is :

var somefunc = person.getName; 
setTimeout(somefunc, 2000);

So somefunc gets called in the context of window. i.e. window.somefunc()

However when you do

setTimeout(function(){
    person.getName()
}, 2000);

The context of getName is preserved as you are Calling (not passing around) person.getName

basarat
  • 207,493
  • 46
  • 386
  • 462
  • What he's effectively doing is `var somefunc = person.getName();`, which sets `somefunc()` to whatever `person.getName()` returns. Context is not the problem, the issue is calling versus referring. – Barmar May 30 '13 at 02:09
1

I have just answerd for something similar here :

How can I pass a parameter to a setTimeout() callback?

The setTimeout function fix the context to the window, so it's not possible to do what you want !

To do it i have wrapped the setTimeout function in another one which can set the context :

myNass_setTimeOut = function (fn , _time , params , ctxt ){
return setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.apply(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// lets try this functions :
for(var i=0; i<10; i++){
   setTimeout(function(){console.log(i)} ,1000 ); // stock setTiemout in closure
}

for(var i=0; i<10; i++){
   setTimeout( console.log(i) ,1000 ); // stock setTiemout direct call 
}

for(var i=0; i<10; i++){
   setTimeout(console.log ,1000 , i); // stock setTiemout not compatible IE
}

for(var i=0; i<10; i++){
   myNass_setTimeOut(console.log ,1000 , [i] , console); // wrapped setTimeout
}

So to answer your question :

var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        console.log(this.first + ' ' + this.last);
    }
}

setTimeout(person.getName(), 2000);

When you launch : setTimeout(person.getName(), 2000); setTimeout will execute in the future 2s (2000ms) the 1st argument !

But what is the value of your 1st argument ? : the result of your function person.getName( ), so it's equivalent of :

var _arg1 = person.getName();
setTimeout(_arg1 , 2000);

which is very different of :

var _arg1 = person.getName;
setTimeout(_arg1 , 2000);

The first case you pass the result of a function to setTimeout which wait a reference to a function. In the second case you pass a reference to a function (good it's what expected), but not in the good context !

So, now you have to fix the context : whith the core javascript function :apply

Now try this :

var _arg1 = function(){ person.getName.apply(person) };
setTimeout(_arg1 , 2000);
myNass_setTimeOut(person.getName , 2000 , null , person);

So you have two choice :

  • Fixing the context for every argument you pass to setTimeout.
  • Use a function which do it for you

the myNass_setTimeOut function will make the trick !

Now, let see something a little bit more deeper :

var person = {
        first: 'joe',
        last: 'doe',
        getName: function(){
            console.log(this.first + ' ' + this.last);
        } ,
        say : function(sentence){
             console.log(this.first + ' ' + this.last + ' say : ' + sentence)
        }
    }

How could pass argument sentence to a setTimeout ?

var heSay = "hello !"; setTimeout(person.say(heSay) , 1000 ); heSay = "goodBye !";
// not good : execute immediatly

var heSay = "hello !";setTimeout(function(){person.say(heSay)} , 1000 ); heSay = "goodBye !";
// not good : hesay googbye

var heSay = "hello !"; setTimeout(person.say , 1000 , heSay); heSay = "goodBye !";
// not good bad context

var heSay = "hello !"; setTimeout(function(whatHeSay){person.say(whatHeSay)} , 1000 , heSay);heSay = "goodBye !";
// GOOD ! ok but not compatible with IE

var heSay = "hello !"; myNass_setTimeOut(person.say , 1000 , [heSay] , person ); heSay = "goodBye !";
// just good !

hope this help you !

edit :

for modern browser suporting bind don't take care about that do what say here @dandavis

Community
  • 1
  • 1
Anonymous0day
  • 2,662
  • 1
  • 11
  • 15
0
  1. you call it too early.
  2. the this gets broken when you save a function ref without the object it's attached to.

solution?

use bind and don't call it early:

var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        console.log(this.first + ' ' + this.last);
    }
}

setTimeout(person.getName.bind(person), 2000);
dandavis
  • 14,821
  • 4
  • 34
  • 35
0

Your first function works fine for me (tested in Chrome):

http://jsfiddle.net/fzrxK/1/

var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        alert(this.first + ' ' + this.last);
    }
}

setTimeout(person.getName(), 2000);

Perhaps there's another issue elsewhere?

calipoop
  • 630
  • 6
  • 26
0

Calling person.getName() executes the function and returns undefined (as nothing was declared to return in that function).

What you wanted was:

setTimeout(person.getName, 2000);

Calling setTimeout(person.getName(), 2000); sets a timeout to execute the results returned by person.getName() which is undefined rather than a function.

Calling setTimeout(person.getName, 2000); sets a timeout to execute the function person.getName.

With parenthesis executes the function. Without parenthesis passes the variable (which happens to be defined as a function).

Edit: As @Isaac points out, passing the function person.getName in also changes the behavior of this so that is likely not what you wanted either.

Shadow Man
  • 2,855
  • 1
  • 19
  • 33
  • `setTimeout(person.getName, 2000)` is what he actually did, otherwise he wouldn't have got the `undefined undefined` output in console… – Bergi May 30 '13 at 01:26
0
var person = {
    first: 'joe',
    last: 'doe',
    getName: function(){
        console.log(this);
    }
}
person.getName()
setTimeout(person.getName, 2000);

Outputs:

Object {first: "joe", last: "doe", getName: function}
Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

My guess is, JavaScript pulls the getName function out of the scope of person

Isaac
  • 10,133
  • 5
  • 27
  • 43
  • You are right: http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/ – Shadow Man May 30 '13 at 01:20
  • 1
    Also, note that `setTimeout(person.getName, 2000);` is different to `setTimeout(person.getName(), 2000);` – Isaac May 30 '13 at 01:24
  • `person` is not a scope. It's the context of the call `person.getName()`, and also that function never was "in" there to be "pulled out" – Bergi May 30 '13 at 01:28
  • http://en.wikipedia.org/wiki/Scope_(computer_science) Well actually, `scope == context`, so I'd still be correct – Isaac May 30 '13 at 01:30