10

this is one of most mystery feature in JavaScript, after assigning the object method to other variable, the binding (this keyword) is lost

var john = {
  name: 'John',
  greet: function(person) {
    alert("Hi " + person + ", my name is " + this.name);
  }
};

john.greet("Mark"); // Hi Mark, my name is John

var fx = john.greet;  
fx("Mark"); // Hi Mark, my name is 

my question is:

1) what is happening behind the assignment? var fx = john.greet; is this copy by value or copy by reference? fx and john.greet point to two diferent function, right?

2) since fx is a global method, the scope chain contains only global object. what is the value of this property in Variable object?

nandin
  • 2,549
  • 5
  • 23
  • 27
  • The binding isn't "lost" -- it was never there in the first place. In any JavaScript expression `foo.bar()`, `this` in the context of the `bar()` function will be `foo`, regardless of where `bar` was declared or how it came to be associated with `foo`. – Daniel Pryden Apr 23 '10 at 21:20

5 Answers5

16

john.greet("Mark") actually calls a function. When you do var fx = john.greet;, you're getting a reference to the function. So when you call it, this is not bound to john. What you're actually doing is window.fx("Mark") and so this is the window object. You were on the right track when you said that it was in the global context. In this particular instance, the global object is window, and so fx is actually window.fx.

When you have a function reference you should use call or apply if you want to set the value of this. Try doing this:

fx.call(john, "Mark");

The first argument in call or apply is the value used for this in the context of the function call.

EDIT

Some people mentioned that the real issue here might be confusion surrounding an object literal vs. an instance of an object. You're creating an object literal which also behaves kind of like a singleton. You cannot create a new instance of that object. In this case john is a reference to that object literal. In that context, this in the function greet refers to the object literal itself. Hence when you call john.greet("Mark"), this is bound to john.

When you grab a reference to john.greet just by itself and assigning it to a global variable, you're essentially doing this:

var fx = function(person) {
   alert("Hi " + person + ", my name is " + this.name);
}

In this scenario, this is window, because fx is basically window.fx (since the global object here is window. Assuming this code was wrapped inside another function, then the global object would refer to that function.

If you want to create multiple instances of an object, you can do something like this:

var Person = function(name) {
    var self = this; //maintains a reference to the instance

    this.name = name;
    this.greet = function(name) {
        alert("Hi " + name + ", my name is " + self.name);
    }
}

var john = new Person("John");
john.greet("Mark"); // alerts "Hi Mark, my name is John"

var fx = john.greet;
fx("Mark"); // also alerts "Hi Mark, my name is John"

Here, the self variable (which is local to the function) maintains a reference to the actual instance because you're binding it to this when you create the object.

There are many best practices associated with OOP in Javascript. You can Google and find out (there are many links). I recommend reading stuff from Douglas Crockford especially.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
  • yes, good mention of .call, which will actually solve the issue 8) – Funkatron Apr 23 '10 at 21:21
  • @funkatron - the issue is far more deeply seated than the manner in which the method is called. Answering a question as written is a good practice. Being able to determine when the questioner doesn't understand own question or problem is also good. – Sky Sanders Apr 23 '10 at 21:35
  • @sky: calm down, dude. I'm sure you're a super pro here, and everyone knows how great you are at answering questions. – Funkatron Apr 23 '10 at 21:58
  • I realize that the main issue here is object literal vs. object instance. But that's not what the OP asked. I'll edit my answer and add that in there because I think it is useful information. – Vivin Paliath Apr 23 '10 at 22:03
  • @funk - not sure how you got the idea that I am not calm. i simply wanted to point out that helping someone do something the wrong way by finding a workaround may not be helpful. in some cases. in my opinion. but not on tuesdays. and it is not tuesday, in my opinion. – Sky Sanders Apr 23 '10 at 22:04
  • @Vivin, even if `fx` is not global (i.e. `var fx = ...` inside a function), the `this` value will still refer to the global object, for [example](http://jsbin.com/ofeco/edit). – Christian C. Salvadó Apr 23 '10 at 22:25
  • @CMS That's true - I should make it clearer. It essentially refers to the global object which *in this case* happens to be the `window` object. – Vivin Paliath Apr 23 '10 at 22:28
6

1) fx and john.greet are referring to the same function object, the assignment operation for objects, works by reference.

For primitive values, like String, Number, Boolean undefined or null, a copy of the value will be made.

2) The this value refers to the global object.

The this value is not a property of the Variable Object and it has nothing to do with the scope chain, is a special reserved word, and it is determined implicitly when a function is called (you can also set it explicitly via call or apply).

JavaScript internally handles a Reference type, which consists of two components, the base object and the property name, when a function is invoked, the this value is determined implicitly by getting the base object (by the internal GetValue operation).

And finally, the last case where this is set implicitly is when you invoke a function with the new operator, the this keyword will refer to a newly created object.

So in brief, here is how this works implicitly:

1- When a function is called as a method (the function is invoked as member of an object):

obj.method(); // 'this' inside method will refer to obj

2- A normal function call:

myFunction(); // 'this' inside the function will refer to the Global object
// or 
(function () {})();

3- When the new operator is used:

var obj = new MyObj(); // 'this' will refer to a newly created object.
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
3

As I understand it, you're only assigning that method to the variable "fx." The context of the john object doesn't come along with it.

Off the top of my head, "this" in the context of fx will refer to the global object, which in the context of a browser is (I believe) equivalent to your window object.

(editing to clarify global object. Sort of)

Funkatron
  • 912
  • 5
  • 13
  • Right, because the global object is `window`, so an unqualified `foo` always means `window.foo` (unless you've scoped it more locally using `var`). – Daniel Pryden Apr 23 '10 at 21:19
  • this will refer to whatever is calling the method, either implicitly or explicitly with `call` or `apply`. – Sky Sanders Apr 23 '10 at 21:37
1

Because you're only setting fx to the greet method and not the entire john object, it has no concept of it's parent and becomes globally scoped. So in essence, it's passing by value in that in only copies the method.

Since the function is now globally scoped, "this" becomes the Window object.

If you instead set fx to john, you get what's expected.

var john = {
  name: 'John',
  greet: function(person) {
    alert("Hi " + person + ", my name is " + this.name);
  }
};

john.greet("Mark"); // Hi Mark, my name is John

var fx = john;  
fx.greet("Mark"); // Hi Mark, my name is John
zachwood
  • 76
  • 1
  • 2
0

inspired by @Vivin Paliath answer, actually I come out something new. As to me, I always try my best to make javascript programming the same way as java, especially in OOP.

So my suggestion is to avoid using this as possible as we can , when we first do

var self = this;

we should use self instead of this in all function (prototype function, whatsoever), but if we write something like this:

function MyObject = {
     var self = this;
};

MyObject.prototype = {
     method1 = function(data){
         self.data = data;
     }
}

This is not gonna work, because prototype is an object in MyObject, It can not access private member self owned by MyObject. My solution for this is simple:

function MyObject = {
     var self = this;
     MyObject.prototype.method1 = function(data){
         self.data = data;
     };
}

This takes the advantage of prototype's efficiency and also we do not have to care about all the this issues. Though we gonna type a lot of MyObject.prototype.xxxx thing.

If this helpful to your guys, please give me some thumb up, so I can gather 15 reputation to thumb up others, thanks.

HMR
  • 37,593
  • 24
  • 91
  • 160
zhouwubai
  • 57
  • 1
  • 6