3

So I am trying to learn functional programming and I see that when I return

const profile = {
  name: 'qw',
  children: [{
    name: 'peter',
    getName() {
      return this.name;
    }
  }],
  getName() {
    return this.name;
  }
};
const val = profile.getName();

console.log(`output is ${val}`); //I get 'qw' as expected


//However if I try 

const val1 = profile.getName;
console.log(`output is ${val1()}`); //I get ''

I am not sure why this is returning different things on not using '()'

Code Maniac
  • 33,907
  • 4
  • 28
  • 50
leo
  • 83
  • 5
  • The issue is `this` does not refer to what you think it does in the `console.log` string template context. – Brandon Jun 11 '19 at 16:44
  • In short, in your second code snippet `this === window`, so you are getting `window.name` – VLAZ Jun 11 '19 at 16:44
  • I understand that , I am just not sure why using `()` helps me out ? Why not the one I entered first. – leo Jun 11 '19 at 16:46
  • @leo check the dupe but in short `profile.getName()` will have `this === profile` so `name === "qw"` while `const val = profile.getName; val()` will execute with `this === window` and thus `this.name` will be `window.name`. – VLAZ Jun 11 '19 at 16:48
  • I mean why is this referring to the window variable @vlaz – leo Jun 11 '19 at 16:49
  • @leo I can't recomment checking the dupe more, it goes in depth. But just to shorten it - JS has delayed context initialisation, so `this` will be given a value *at the time of invocation*. It will have the value you invoke it from. When you get the function handle via `const val = profile.getName;` you lose the original context which is `profile` in your first example. When there is no context, `window` is used by default. Or rather, the global object which, in browsers, is `window`. You can see more details about this in the dupe. – VLAZ Jun 11 '19 at 16:53

2 Answers2

1

Adding to @VLAZ's comment, const val = profile.getName(); returns the result of the invocation (this is within the profile context) while const val = profile.getName; just references getName in profile.When you do invoke it, then the context is window.

In other words profile.getName() executes (using the correct this) while profile.getName does not execute. And when it does, the context is window

const profile = {
    name: 'qw',
    children: [
        {
            name: 'peter',
            getName() {
                return this.name;
            }
        }
    ],
    getName() {
        return this.name;
    }
};
const val = profile.getName();
// this is the equivalence of "qw" which is the result of invoking `getName`

console.log(`output is ${val}`); // 'qw'

const val = profile.getName;
// this is the equivalence of:

  getName() {
     return this.name;
   }
// And the context here is the window.   
console.log(`output is ${val}`); // ''
Tony M
  • 656
  • 4
  • 16
  • 1
    The context doesn't "change" - it's only set at the time the call is made. So when `profile.getName()` runs, it will invoke `getName` with a context of `profile`. When running `getName` with no context (in the second example), the context is set to `window`. There is no change - `this` inside the body of `getName` was never a concrete thing, it would always be determined at a later point. – VLAZ Jun 11 '19 at 17:07
  • Great point. Scope in JS is a bit tricky. Most explanations are too convoluted and it's often times difficult to fully understand (clearly I have some knowledge holes). I think the OP's confusion lies with invoking `getName` i.e. `getName()` vs `getName` – Tony M Jun 11 '19 at 17:10
0

Despite the short syntax, getName() is still just a regular function, which happens to be stored in an object. It does not carry an object reference around, this is evaluated when/where/how you call it:

var obj1={
  test:"This is obj1",
  logTest(){console.log(this.test);}
};

obj1.logTest();

var fun=obj1.logTest;
fun();
var test="This is window";      // in browsers, regular variables on the top-level
fun();
window["test"]="Indeed it is";  // are actually members of window
fun();
this.test="All the same";       // really they are
fun();

var obj2={
  test:"This is obj2",
  testfun:fun
};
obj2.testfun();

//////////////////////////////////////
console.log("--------");

var boundfun=obj1.logTest.bind(obj1);
boundfun();

var obj3={
  test:"This is obj3",
  testfun:boundfun
};
obj3.testfun();

However the last part, boundfun(), shows a method of function objects, bind() which you can use to "pre-parametrize" a function, where the first parameter you can set is the this. I also suggest checking the other two methods linked on the left side of that page, call() and apply(), both are about invoking a function via not just passing its arguments, but also freely setting this. So you could create your own bindthis() if you wanted to:

var obj1={
  test:"This is obj1",
  logTest(){console.log(this.test);}
};

function bindthis(fun,diz){
  return function(){
    fun.call(diz);
  }
}

var boundfun=bindthis(obj1.logTest,obj1);

boundfun();

var obj2={
  test:"This is obj2",
  testfun:boundfun
};

obj2.testfun();

Side note: the question is not really about functional programming, this is an object-oriented concept. So what you see is more like one particular mix of functional and object-oriented programming.

tevemadar
  • 9,697
  • 2
  • 15
  • 36