2
var name = 'bob';
var someObject = {
    name: 'james',
    someProperty: {
         name: 'sam',
         getName: function(){
             return this.name;
         }
    }
}

var testing = someObject.someProperty.getName;

testing();

Is the reason this block of code returns 'bob' because we are just ultimately calling this.name on a global object's name, which is 'bob' or is there a better way to think about this problem? Thanks!

  • 1
    __What is the question ?__ _The value of `this` is determined by how a function is called._ – Rayon May 23 '16 at 05:42
  • testing is in global scope, or you need to bind it – YOU May 23 '16 at 05:44
  • 1
    @Rayon I think OP wants to know why the output is "bob" instead of the name of `someProperty` ("sam"). – diiN__________ May 23 '16 at 05:45
  • Possible duplicate of [How does the "this" keyword work?](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – JJJ May 23 '16 at 05:47

6 Answers6

4

The value of this is determined by how a function is called.

testing() is invoked as window.testing() hence this refers to window and as var name is under the global(scope of window), "bob" is returned.

You can use [.call'(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) of ES5 to specify this context to get "sam".

The call() method calls a function with a given this value

Use .bind() to have a specified this-reference context in function-body, The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

var name = 'bob';
var someObject = {
  name: 'james',
  someProperty: {
    name: 'sam',
    getName: function() {
      return this.name;
    }
  }
}

var testing = someObject.someProperty.getName;

console.log(testing()); //bob
console.log(testing.call(someObject)); //james
console.log(testing.call(someObject.someProperty)); //sam

console.log('------OR--------')

var testing = someObject.someProperty.getName.bind(someObject);
console.log(testing());
var testing = someObject.someProperty.getName.bind(someObject.someProperty);
console.log(testing());
Rayon
  • 34,175
  • 4
  • 40
  • 65
1

Running the above code in Chrome Dev Console I got the following output:

Input:

var testing = someObject.someProperty.getName;
testing();

Output:

"bob"

But then I changed as:

Input:

var testing = someObject.someProperty.getName();
testing;

Output:

"sam"

Well, in the first scenario, "testing" becomes a function object which will the return the value of global "name" variable. "testing" has nothing to do with "someObject" in this case. Which is similar to:

// global scope
var testing = function() {
    return this.name;
}

In the second scenario, "testing" is simply the returned value of "someObject.someProperty.getName()".

nimteaj
  • 410
  • 7
  • 19
0

When you call testing, the function is being invoked without an object context and the global context is used for the this pointer. this.name on the global context is 'bob' and hence the result. Calling someObject.someProperty.name() would return 'sam'. If you want it to return 'james' you need to call it like this:

someObject.someProperty.getName.call(someObject);

or:

var testing = someObject.someProperty.getName.bind(someObject);
testing();
Anil Kumar
  • 104
  • 6
0

var name = 'bob';
var someObject = {
   name: 'james',
   someProperty: {
      name: 'sam',
      getName: function() {
         console.log(this);
         return this.name;
      }
   }
}
var testing = someObject.someProperty.getName;
testing()

Window {external: Object, chrome: Object, customElements: undefined, originalOutline: undefined, someObject: Object…}

When I tried that, noticed how the result of my (this) was printed, and it refers to the global "this". This isn't what we want. If I wanted to print "sam", I would do this to call it with a way to redefine what "this" means.

 testing.bind(someObject.someProperty)(). // returns "sam"

In addition you can also do this:

var name = 'bob';
var someObject = {
   name: 'james',
   someProperty: {
       name: 'sam',
       getName: function() {
          return this.name;
       }.bind(someObject.someProperty)
    }
 }
 var testing = someObject.someProperty.getName;

This will return "sam" when you call it, regardless of the "this" context.

Another way of doing this is to keep your original code, but instead of setting

var testing = someObject.someProperty.getName;

instead change it to:

var testing = someObject.someProperty.getName();

This way now the object refers to the getName() function using the context of your someProperty.

ngoctranfire
  • 653
  • 7
  • 14
0

I believe you are partly mixing up this simple object literal with object constuctors. The object literal someObject does not automatically engage this - its pretty much the same as a bunch of variables.

var name = 'bob';
var someObject = {}
someObject.name = 'james'
someObject.someProperty = {};
someObject.someProperty.name = 'sam';
someObject.someProperty.getName = function(){
    return this.name; // global context
}

You can approximate instancing, _and thus engage this to in javascript in a few ways:

Using Object.create

Passing the object literal someObject to Object.create() will create a new context for this as you might expect:

var name = 'bob';
var someObject = {
    name: 'james',
    someProperty: {
         name: 'sam',
         getName: function(){
             return this.name;
         }
    }
}

var aNewContext = Object.create(someObject);

aNewContext.someProperty.getName();
// "sam"

Using Function constructors

When you use the keyword new with a function, it also creates a new object and assigns it to this. Functions added to the prototype can be called as methods, will also reference this:

function SomePropertyConstructor() {
    this.name = 'sam';
}
SomePropertyConstructor.prototype.getName = function(){
    return this.name;
}


var name = 'bob';
var someObject = {
    name: 'james',
    someProperty: new SomePropertyConstructor()
}


someObject.someProperty.getName();
// "sam"

Note - if you call the function on its own, you will need to control the context using bind:

var unbound = someObject.someProperty.getName;
[ unbound(), unbound.bind(someObject.someProperty)()]
//["bob", "sam"]
Ashley Coolman
  • 9,223
  • 4
  • 47
  • 68
0

Just to add on, if you want to use a function to get sam, you can wrap the function call in another function:

var testing = someObject.someProperty.getName;

testing(); // "bob"

function getSam(){
    return someObject.someProperty.getName();
}

getSam(); // "sam"
paradite
  • 5,641
  • 3
  • 35
  • 54