5

I have a simple object and I don't understand the concept(scope) of this by calling the functions of this object.

Why in the last variant (3) calling show() (with the function show() inside object without parent) the result is "This is global" and not the inside variable title("Color Picker")?

I have the vague idea that calling the function popup.show() after the gloabal variable show is defined, this refers to the global object. Is this the logic explanation?

The code: http://jsbin.com/otuzac/1/edit.

var title="'This' is global";

var popup={
    dom_element:("#popup"),
    title      :"Color Picker",
    prev_color :'#fff',
    set_color  : function(color){
        color=color || this.prev_color;
        //set the color
        return color;
    },
    show       :function(){
        return("showing  "+this.title);
    }       
};

var show=popup.show();

//Case 1: var show=popup.show()
alert(show);  //output "Color Picker"

//Case 2: var show=popup.show()
alert(popup.show());  //output "Color Picker"

//Case 3: var show=popup.show !!! (no parent.)
alert(show());  //output "This is global"
Noctis
  • 11,507
  • 3
  • 43
  • 82
Alexis
  • 498
  • 4
  • 13

5 Answers5

5

What this is depends on how you call the function in which you use this.

1. call as a function

functionName();

In this case this will always refer to the global object (most often the window object).

Example

a = 2;
function XY(a) {
    this.a = a;
    this.b = function () {
        func();
    };

    function func () {
        console.log(this.a);
    }
}

var xy = new XY(1);
xy.b(); //2

Remarks

  • The example is a little bit constructed, but note, that the function func is called by simply write func();. So even if your function is places inside a constructor function (XY) and called from a function that is called as a method (see point 3), this still refers to the global object.

2. call with new keyword

var obj = new functionName();

In this case this will refer to the new created object.

Example

a = 2;
function XY(a) {
    this.a = a;
}

var xy = new XY(1);
console.log(xy.a); //1

3. call as a method

obj.functionName();

In this case this will refer to the object that contains the function you are calling.

Example

a = 2;
var xy = {
    a: 1,
    func: function() {
        console.log(this.a);
    }
}
xy.func(); //1

4. call by using apply

functionName.apply(thisObj, argArray);

In this case this will be new Object(thisObj), with thisObj being the first argument of the function apply.

Example

function xy (a,b) {
    console.log(this);
}

xy.apply({f:3}, [1,2]); //Object {f: 3} 
xy.apply("hello", [1,2]); //String {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

5. call by event handler

As the user Mifeet suggested, events also change the context of this:

Events are kind of a separate topic. They are not part of EcmaScript and are generally handled by different browsers differently. Nevertheless, the differences concerning this are minor and for IE > 8 as far as I know nonexistent.

this will refer to the DOM element that fired the event, except when you use inline event handler. In that case this will refer to the global object.

Example

<button id="1" onclick="clickit()">click me</button>  <!-- 0 -->
<button id="2">click me</button>
<button id="3">click me</button>
<script>
var button1 = document.getElementById("1");
var button2 = document.getElementById("2");
var button3 = document.getElementById("3");

id = "0";

window.clickit = function(){
    console.log(this.id);
};


button2.onclick = clickit; //2
button3.addEventListener("click", clickit, false); //3
</script>

Remarks to Events

  • Internet Explorer prior to version 9 didn't support addEventListener but attachEvent. Using this function will also result in this refering to the global object.
  • this, directly inside the HTML tag, will refer to the DOM element representing that tag. Therefore <button id="1" onclick="clickit(this)">click me</button> will pass the DOM element to the clickit event handler.

back to your case

In your first two cases, you are calling your function as a method and in the last case, you are calling it as a function. That is why in the last case, this refers to the global object.

EDIT

I recently saw a very similar answer to a very similar question here on StackOverflow. Unfortunately I couldn't find it anymore, so I decided to post an answer by myself. But if someone knows what answer I mean, just leave a comment and I'll be happy to put a link to the original answer in my answer.

basilikum
  • 10,378
  • 5
  • 45
  • 58
  • What about event handlers, isn't it a special case? – Mifeet May 30 '13 at 11:15
  • Question: in your first case the function is actualy a method. Am I missing something?! Besides you called it with new operator which creates an object, so must be a method of this object... – Alexis May 30 '13 at 11:24
  • @Alexis The Function `func` is called as a function. It is called inside another function, that is called as a method. I just wanted to show that it doesn't matter where you define your function and where you call it, as long as you call it as a function by just writing `func()`, the `this` object inside that function will refer to the global object. – basilikum May 30 '13 at 11:29
  • @Mifeet Yes you are right, this would be a fifth case, in which the `this` keyword refers to the DOMElement, that the eventlistener was assignd to. – basilikum May 30 '13 at 11:34
  • OK, i guess i got it. Yea, the events are another story, and this inside events means the object who dispatched the event i guess – Alexis May 30 '13 at 11:35
3

In your case 1: var show=popup.show() is calling the function show that is defined in popup object. And show function there is this object that is refer to popup object and popup object has its own title variable that is calling on show function. that's why the is showing "color picker".

case 2: this is also silimar to case 1 here you are calling show function without using the variable. so the same output.

case 3: In case third you are calling the function show without refering any object as you are doing in the case 1 and 2. so default it is taking the object od document. and in document the value of title is "'This' is global" so it is showing you the result "'This' is global". and this is refering to the document object.

Hope this will be helpful to you.

Code Lღver
  • 15,573
  • 16
  • 56
  • 75
1

That's because you treat the variable show as a function. Let's make this simpler :

function a(){
 return "textA";
}

var obj={
  b: function(){ return "text b";}
};

console.log(a); // will output the function object
console.log(a()); // will call a then output the result of the function --> "textA"
console.log(obj.b); //will output the function object same as console.log(a)
console.log(obj.b()); // will call b then output the result of the function --> "text b" 

Now imagine I do something like your 3rd example :

var anotherObj = a();
console.log(anotherObj()); // you try to treat the return of a() as a function. will not work --> "string is not a function "

You could do this for example :

console.log(window[anotherObj]());

this would call the function named "textA". Full Fiddle here

As for the scope, this refers to the window by default. When runnning inside an object, this will refer to this object unless the function is applied to something else.

Community
  • 1
  • 1
TecHunter
  • 6,091
  • 2
  • 30
  • 47
1

As explained by others, in the first two cases you call the function on the popup object, therefore this refers to popup. In the third case you call the function alone, not on an object, therefore this refers to the global scope (window). In addition, in your example, you cannot use alert(show()); because show here is not a function but a String.

If you want to have the desired result, you can use the following pattern (used by many frameworks, e.g. jQuery):

var title="'This' is global";
(function() {
    var popup = { 
        dom_element:("#popup"),
        title      :"Color Picker",
        prev_color :'#fff',
        set_color  : function(color){
            color=color || this.prev_color;
            //set the color
            return color;
        },
        show       :function(){
            return("showing  " + popup.title); // refer to the local variable popup instead of this
        }       
    };
    window.popup = popup; // add the (now initialized) local variable popup to the global scope
})(); // execute immediately

var show=popup.show();
var showFn = popup.show; // You cannot use the result of popup.show() as a function because it's a string

//Case 1: 
alert(show);  //output "Color Picker"

//Case 2: 
alert(popup.show());  //output "Color Picker"

//Case 3: 
alert(showFn());  // output "Color Picker"

(JS Bin)

Mifeet
  • 12,949
  • 5
  • 60
  • 108
0

this always refers to the current object or context.
In case 2 that object is popup. So that is why you see popup.title in the alert.

What you are effectively doing in case 3 when you assign popup.show to show is creating a handle on the window object to show ( or at least the current scope ). So for that handle the object is window. So now if you call this handle it will show the title that you also registered on the window object as well.

You can set the context with call and/or apply like this:

var show=popup.show 
alert(show.call(popup));
Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99