0

I'm trying to set timeout to a function of an object, which is a private function of another object. Actually the structure is something like this:

function MainObject() {
    var id;
    ...
    var InnerObject = function() {
        this.getSomething = function() { ... }
    }

    this.testFunc = function() {
        id = setTimeout(InnerObject.getSomething(), 1000);
    }
}

...and it won't work as it says 'undefined' when trying to set timeout, and I can't execute this function directly as it's a private function. What am I doing wrong? Tried to find something particular but didn't success. I see the error might be somewhere in scope problems, but still don't get it

Phialco
  • 13
  • 3
  • First issue: You are calling InnerObject.getSomething() not assigning it. – epascarello Nov 03 '14 at 18:26
  • 2
    `InnerObject` looks like a constructor function, but you never created an object with that constructor (e.g. you never called it). There is no `InnerObject.getSomething` property. If you did `var obj = new InnerObject();`, then you could do `obj.getSomething()`. Also, keep in mind that what you need to pass to `setTimeout()` is a function so unless `.getSomething()` returns a function, you don't want to execute it. – jfriend00 Nov 03 '14 at 18:28
  • 1
    Such a disappointing question. There are a bunch of things wrong here and it looks like you're using an overly complicated approach to doing whatever you're trying to do, but because you give us NO info on what problem you're really trying to solve or why you've gone down this complicated path, we don't really know what exactly to suggest to "do it the right way". – jfriend00 Nov 03 '14 at 18:32
  • Sorry for no info, it's a long way to explain all actually, but the thing is I have one object with some functions implemented, and there's another object that uses functions of the first one, and I don't want it to be accessible, so I thought it would be more or less easier to set it as a private object of the main one. Yeah it's a bit overcomplicated, I don't have too much experience in that, sorry :D – Phialco Nov 03 '14 at 18:42
  • And yes, now it works with a variable associated, thank you. PS. What else actually seems to be wrong here? – Phialco Nov 03 '14 at 18:44
  • @Endym - I posted in my answer. – jfriend00 Nov 03 '14 at 18:57

4 Answers4

1

Since you've given us little to go on for why you've chosen a complicated approach, I will simplify things into something that will work.

function MainObject() {
    var id;
    ...
    function getSomething() {
        // code here
    }

    this.testFunc = function() {
        id = setTimeout(getSomething, 1000);
    }
}

Things wrong with your approach:

  1. InnerObject looks a constructor function, but you never actually call it.
  2. Because InnerObject is a constructor function, there is no InnerObject.getSomething property. If you did var obj = new InnerObject(), then there would be an obj.getSomething property you could call.
  3. You pass a function reference to setTimeout(). Unless getSomething() returns a function, you don't want to call it, you would want to pass just the function reference without the parens.

If you really wanted to keep the InnerObject, then you'd have to actually instantiate an object with that constructor like this:

function MainObject() {
    var id;
    ...
    var InnerObject = function() {
        this.getSomething = function() { ... }
    }
    // call your constructor and create an object
    var obj = new InnerObject();

    this.testFunc = function() {
        id = setTimeout(obj.getSomething.bind(obj), 1000);
    }
}

Note: the .bind(obj) is used so that the this pointer is set correctly when getSomething() is called by the timer code.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Oh thank you, it works with bind(obj) function! It's like the function I passed to setTimeout worked only once (despite that it actually was a function). Apparently, thing really needs to be simplified. Thank you :) – Phialco Nov 03 '14 at 19:09
0

You have InnerObject set up as a constructor, so it needs to be initialized. And setTimeout expects a function, but you're actually invoking the function.

function MainObject() {
    var id;

    var InnerObject = function() {
        this.getSomething = function() {
          console.log('getSomething called');
        };
    };

    this.testFunc = function() {
        id = setTimeout((new InnerObject()).getSomething, 1000);
    };
}

var o = new MainObject();
o.testFunc();

Demo: http://jsbin.com/lozolujatu/1/edit

Miguel Mota
  • 20,135
  • 5
  • 45
  • 64
0

You're calling getSomething() on the constructor, not the object. It doesn't look like you ever actually instantiate InnerObject, actually. If InnerObject is supposed to point to whatever this function returns (instead of to the actual function), then you need to make InnerObject into an immediately-invoked function expression (IIFE), like so:

function MainObject() {
    var id;
    ...
    var InnerObject = (function() {
        this.getSomething = function() { ... }
    }());

    this.testFunc = function() {
        id = setTimeout(InnerObject.getSomething(), 1000);
    }
}

Now InnerObject points to the result of calling the function you wrote, which will have a this.getSomething member that you can see in the constructor (but nowhere else, since InnerObject is still just a variable inside the constructor).

The catch here is that because I used an IIFE, InnerObject only gets instantiated once. If you need to instantiate several of them from the same constructor, then you need to keep a handle around. You can still do this while being private, however. One possibility might look like:

function MainObject() {
    var id;
    ...
    function InnerObject() {
        this.getSomething = function() { ... }
    };

    var inner1 = new InnerObject(),
        inner2 = new InnerObject(),
        inner3 = new InnerObject();

    this.testFunc = function() {
        id = setTimeout(inner1.getSomething(), 1000);
    }
}

inner1, inner2, and inner3 are all still private, as is the InnerObject constructor. I just picked one for this.testFunc to work on.

The Spooniest
  • 2,863
  • 14
  • 14
  • I instantiated a variable as you did with inner1,.. but now the setTimeout function won't work propetly (actually, the getSomething() function invokes only once). The problem seems to he harder than I expected :/ – Phialco Nov 03 '14 at 19:03
  • setTimeout() only invokes the function once. If you need to invoke it repeatedly, then you need to either use setInterval() or use make another call to setTimeout() at the end of getSomething(). There are advantages and disadvantages to both approaches. – The Spooniest Nov 03 '14 at 19:55
-2

As seen here you can send setTimeout either a anonymous function or an expression. You were sending it a function, which returns a value, then that value was being sent as the argument to setTimeout().

As an expression:

id = setTimeout("InnerObject.getSomething()", 1000);

As an anonymous function:

id = setTimeout(function(){InnerObject.getSomething()}, 1000);

Also, as jfriend00 in the comments pointed out, this would only work if you created an InnerObject type, like this:

id = setTimeout(function(){
    var innerObj = new InnerObject();
    innerObj.getSomething();
}, 1000);
halfer
  • 19,824
  • 17
  • 99
  • 186
phantom
  • 1,457
  • 7
  • 15
  • There is no `InnerObject.getSomething` property. Simply doesn't exist so this will never work. – jfriend00 Nov 03 '14 at 18:30
  • @jfriend00 I see, give me a second to fix my answer. It does expose an other error with the code though. – phantom Nov 03 '14 at 18:32
  • AFAIK, anonymous functions pretend to use global context so I can't use it here as I'm trying to acces a private object – Phialco Nov 03 '14 at 18:33
  • Whatever else the problem is here, do not pass string functions to `setTimeout`. –  Nov 03 '14 at 18:35