1

I have a function localised to the main function and i want to use this to call it but it doesn't seem to work.

My code has:

function option(room,slot){

    var div_id = document.getElementById(room);
    var opacity = window.getComputedStyle(div_id).opacity

    transition_opacity(div_id,opacity,0,function(){this.load});


    function load(){
    console.log('test'); //does not happen      
       }
}

Have i misunderstood the use of this or is the scope lost when i use function(){} to call load?

Sir
  • 7,735
  • 12
  • 72
  • 138
  • One can use `bind` or "keep" a context irregardless of how the function is called (although closed over variables will also work). Anyway in this case, you could actually omit the anon function (or any context preserving) entirely because the `load` function does not utilize any special context. – user2864740 Oct 17 '13 at 21:36
  • `this` in functions always points to the global `window` (as much as I remember), except you are creating a class (template). – Derek 朕會功夫 Oct 18 '13 at 15:58

5 Answers5

2

From your code it is not obvious, what object this could refer to. It depends on how option is called. However, if you define the load function inside of the option function anyway, it is best to just reference it directly. You will have to move the declaration of test above the transition_opacity call though:

function option(room,slot){
    var div_id = document.getElementById(room);
    var opacity = window.getComputedStyle(div_id).opacity;

   function load() {
       console.log('test');
   }

   transition_opacity(div_id,opacity,0,load);
}

As you can see, I just reference load directly. You could make another function which calls the load function inside (i.e. function() { load(); } – note the parentheses which calls the function) but that would give you no benefit but would just add another unneeded function to the stack. So just refer to the actual function itself.

For more information on the this keyword, check out this question. Spoiler: It’s more complicated than you would expect.

Community
  • 1
  • 1
poke
  • 307,619
  • 61
  • 472
  • 533
  • I was using `this` to basically force it to use `load` that was local to `option` encase there was a function with the same name declared further up in the scope... does it automatically always use local defined functions first? – Sir Oct 17 '13 at 21:38
  • Scoping rules will always make the interpreter look up from the inner-most scope to the outer-most scope. So you can basically *hide* objects in an outer scope by defining something within a closer scope. In your case, yes, all variables defined within your `option` function will hide any other variables defined outside of it, so referencing `load` (after it has been declared) will always reference that function. And as I said, `this` depends a lot on how a function is called. In your case, this wouldn’t have worked anyway. – poke Oct 17 '13 at 21:40
  • Also, it wasn't necessary to use `function(){this.load}` in `transition_opacity(div_id,opacity,0,function(){this.load});` because load is a function. As pointed out in the answer `transition_opacity(div_id,opacity,0,load);` is sufficient because load is a function. The scope of `this` is "lost" because the context has actually changed (at that point) to the anonymous function. – EtherDragon Oct 17 '13 at 21:46
1

The scope of this is lost in this instance, probably pointing to the document. You can capture this to a variable in the outer scope to make this work as intended.

var context = this;
transition_opacity(div_id,opacity,0,function(){context.load();})

The above will not work however. This is because load does not exist on the context of this. You would need to define the load function as such:

context.load = function(){
   console.log('test');
}
Justin Bicknell
  • 4,716
  • 16
  • 25
  • 2
    And you actually want to call that function. – poke Oct 17 '13 at 21:33
  • @poke - yup missed that. Fixed answer to call the function assuming that's what is intended here – Justin Bicknell Oct 17 '13 at 21:34
  • isn't `this` in the outer scope (of `option`) already though ? – Sir Oct 17 '13 at 21:38
  • `this` refers to the current owner of the function being executed, in this case is not what you were expecting. So no, `this` is not defined in the outer scope – Justin Bicknell Oct 17 '13 at 21:40
  • `this.load` probably won’t be defined though; at least not given OP’s code. So this won’t work. – poke Oct 17 '13 at 21:41
  • @poke - That is true, I was assuming the use of the new keyword.. better off just removing the context – Justin Bicknell Oct 17 '13 at 21:44
  • @JustinBicknell But even when creating an instance, unless `test` is added to the `this` object (`this.test = function() {…}`), it still won’t be visible from the outside. – poke Oct 17 '13 at 21:46
1

Both.

First, your load function is not a member/property of any this, the way you have it coded. Your load function is simply a nested function that exists within your option function, as has been sort of implicitly noted in other responses.

In your option function, if you want 'load' to become a member of 'this', you'd need to say so, like this:

function option(){
    this.load = function(){};  // now load is actually a property of whatever this is
}

Second, you and the other poster are correct that 'this' is no longer the same 'this' by the time your anonymous function is called.

Whenever you call a function, a brand new 'this' is created and exists within the scope of that function. If you just call a function like this:

transition_opacity(args);

.. then within transition_opacity, 'this' just refers to the window object, or maybe window.document. For 'this' to refer to anything other than window or window.document, you need to (in effect) do one of the following:

myObject.transition_opacity(args);

transition_opacity.call(myObject, arg1, arg2, ..);

transition_opacity.apply(myObject, argArray);

or

var myObject = new transition_opacity(args);

In each of those cases, within transition_opacity, 'this' refers to myObject (or, well, in the last case, it refers to a new object that is being created and assigned to myObject).

Here is a way to do what it looks like you're trying to do:

var MyNamespace = {

    option: function(room,slot){
        var div_id = document.getElementById(room);
        var opacity = window.getComputedStyle(div_id).opacity;

        var _this = this;
        transition_opacity(div_id,opacity,0,function(){
            // Careful! Inside here, 'this' is just window or window.document,
            // unless transition_opacity sets it to something using call or apply, 
            // in which case that 'this' is probably not the 'this' you want.
            // So carefully refer to the saved instance of 'this':
            _this.load();
        });
    },

    load: function(){
        console.log('test'); // now it should happen   
    }
}

.
.
MyNamespace.option(room, slot);  // inside option, 'this' is MyNamespace.

Here's another way to do it:

function MyClass(){};
MyClass.prototype = {
   // all the same stuff that is in MyNamespace above..
};
.
.
var myObject = new MyClass();
myObject.option(room, slot);

Clear as mud?

Shavais
  • 2,158
  • 1
  • 25
  • 24
0

Just use

transition_opacity(div_id,opacity,0,load);
Oriol
  • 225,583
  • 46
  • 371
  • 457
0

You have defined a 'load' within another function as an 'Function Declaration', so now it is only accessible within 'option' function and in other functions defined in this one by name 'load'. You can't access it by using 'this.load' no matter what 'this' is. If you want to access 'load' function as 'this.load' you can try this example to understand how 'this' keywoard works

// Function Declaration
function f1(callback){
  callback();
};
// Function Declaration
function f2(){
  // Function Expression
  this.load = function(){
    console.log("test");
  };
  f1(this.load);
};
var obj = new f2(); // test, this == obj, so obj.load() now exists
obj.load(); //test, this == obj
f2(); //test, this == window, so window.load() now exists
load(); //test, window is the global scope
zoberg
  • 65
  • 1
  • 4