1

The following code (codepen) doesn't work.

"use strict"; //without strict "this" works, but it refers to window object

let person = {
  name : "Shimon",
  logName : function(){
    //console.log("test") //this works
    console.log(this.name); //doesn't work
  }
};

//person.logName(); //works
(false || person.logName)(); //doesn't work. Uncaught TypeError: Cannot read property 'name' of undefined

I want to understand why.
When I call (false || person.logName)(); I suppose to call person.logName(), and it indeed called.
So why I cannot use this inside this method?

Shimon S
  • 4,048
  • 2
  • 29
  • 34
  • Possible duplicate of [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – bhspencer Nov 05 '19 at 21:38
  • Instead of logging this name. You can actually return it. There might be problems with before es2015 standard where you refered to this as self. So generate es2015 method instead person = { name:"Simon", logname(){return this.name}} – Poselsky Nov 05 '19 at 21:38
  • @bhspencer It seems to be a very global question. I cannot find a point there. I will be very grateful, if you will specify a more precise manner. – Shimon S Nov 05 '19 at 21:43

4 Answers4

2

This is simply how Javascript semantics behave. Unless you call the method from the object it is attached to this cannot be resolved in the function scope.

Your example of:

(false || person.logName)();

Could be rewritten as:

var func = person.logName;
func();

Here you can visually see that there is an intermediate step where logName() is detached from person. This has a side effect of removing the this context.


There are ways to enforce the this context on "detached" functions. One of them being Function.prototype.bind() which enables the binding of this to any function.

(false || person.logName.bind(person))();

Here a new function is created with person bound as this to achieve the desired behavior. Alternatively you could use Function.prototype.call() to avoid creating a new function.

(false || person.logName).call(person);

This works in your contrived use-case, but it may not fit into your real use-case as you will need to pass different objects as the this parameter when the functions you "detach" are not from the same object.

Jake Holzinger
  • 5,783
  • 2
  • 19
  • 33
2

The are 4 rules for the JavaScript engine to determine the this reference.

  1. New Bound: Was the current function called using the new keyword
  2. Explicit Binding: Was the function called using Function#call or Function#apply.
  3. Implicit Binding: Was the function directly on its owning object?
  4. Default: If in strict mode undefined otherwise global

In your case person.logName() falls under the 3rd category so the this reference is set to the value of person.
But in the other case, the line (false || person.logName)() is equivalent to var x = false || person.logName; x();. That is because you are using an expression to get the function and thus the context is lost.
So you either need to use person.logName() or (false || person.logName).call(person)

Here is a more complex example:

var actionMap = {
  0: function() {
    console.log(this === actionMap ? "actionMap" : "global");
  },
  1: function() {
    console.log(this === actionMap ? "actionMap" : "global");
  },
};

function other() {
  console.log(this === actionMap ? "actionMap" : "global");
}

actionMap[0](); // works
(actionMap[0])(); // works, brackets get ignored
(actionMap[0] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1]).call(actionMap); // works, context is called explicitly

var boundOther = other.bind(actionMap);
(boundOther || actionMap[1])(); // function is bound

As you can see in this example, the context is lost even though both functions are owned by the same object. Since the function that gets called is derived by an expression and is not immediately called on its owning object.

nick zoum
  • 7,216
  • 7
  • 36
  • 80
-1

this works on instantiated classes/functions. In your case you have to treat the object as static like:

let person = {
  name : "Shimon",
  logName : function(){
    console.log(person.name);
  }
};
Kubwimana Adrien
  • 2,463
  • 2
  • 8
  • 11
-1

If you want this behavior you can either use classes or prototypal inherintance

class Person {
  constructor(name) {
    this.name = name;
  }

  logName() {
    console.log(this.name);
  }
}

or

function person(name) {
  this.name = name;
}

person.prototype.logName = function() {
  console.log(this.name);
}
let shimon = new Person("Shimon");
shimon.logName()  // >> Shimon