Does arrow function returned by constructor function have clousure of this key word

Viewed 83

I know how new operator works, it follows 5 steps:

  1. Create a new blank object, let's call the new object as obj
  2. link prototype object of constructor function to the property __proto__ of the new object
  3. bind this to the newly created object obj
  4. if the function doesn't return an object, it will return this(newly created object), otherwise, it will return the value in return statement of the function

and I know arrow function ()=>{} does not have its own bindings to this, so it will find this in the outer lexical environment.

Here is my questions:

Question 1: Why the results of person() and foo() are different? ( the former is Person Object, the latter is Window Object )

function Person() {
  const bar = () => {
    console.log(this);
  };
  return bar;
}

const person = new Person();
person(); // Person

const foo = Person();
foo(); // Window

Question 2: After the new + Person() finish executing and return arrow function bar, does bar have closure of this which belongs to the scope of constructor function Person? and, will this which binded to the newly create object obj instead of the variable bar be destoryed?

2.1 If bar(assigned to person) have not closure of this, then how person() find this when executing?

2.2 If this have not be saved and not be returned to person, why console.log(this) return Object Person?

for example:

function Person() {
    let x = 10;
    const bar = () => {
        console.log(this);
        console.log(x);
    }
    return bar;
}

const person = new Person();
person(); // Person,Closure: {x: 10};

const foo = Person();
foo(); // Window, Closure: {x: 10};

person() have to find variable x in the outer lexical environment (lexical environment of function Person), so it have closure of x.

Since person() have to find this in the outer lexical environment too, will it have closure of this which in the scope of function Person?

If the anwser of the question is true, then person() will find this which bind to obj(Person Object) created by new key word. And foo have closure of this which delivered by Person and binded to Window Object. I think it's the only way to explain why person() have result Person, and foo() has result Window

2 Answers

As per MDN, the binding step is the key:

Binds the newly created object instance as the this context (i.e. all references to this in the constructor function now refer to the object created in the first step).

You can think of the implementation of new as something like:

function nouveau (constructor) {
  // 1. Creates a blank, plain JavaScript object.
  const object = {}; 

  // 2. Adds a property to the new object (__proto__) that links to the
  //    constructor function's prototype object
  Object.setPrototypeOf(object, constructor.prototype);

  // 3. Binds the newly created object instance as the this context (i.e. all
  //    references to this in the constructor function now refer to the object
  //    created in the first step).
  const boundConstructor = constructor.bind(object);

  // 4. Returns this if the function doesn't return an object.
  const maybeResult = boundConstructor();
  if (maybeResult instanceof Object) {
    return maybeResult;
  }

  return object;
}

As you'd noted, arrow functions take their this value from their surrounding lexical scope, which means that unlike a normal function returned from a function used as a constructor, they will have their this as bound in the invocation specific boundConstructor above.

The fact is that unbound functions have no this (or, in non-strict mode, this refers to the top level object, so Window for example). The surprising thing about the scenario you're describing is that it appears to be binding this on an arrow function, but it is actually binding this on the surrounding normal function, thus providing the lexical scope that it sources this from.

Using strict mode makes this a bit clearer, highlighting the fact that unbound function have no this:

'use strict';

function Foo() {
  console.log(this);
}

Foo.bind(42)(); // Number { 42 }

Foo(); // undefined

From the docs :

Constructor functions should only be called using new. Such a call implies creation of an empty this at the start and returning the populated one at the end.

By this I understand that if no empty this is created. this will point to the global object(window).

You can test it like this:

function Person() {
  console.log(this);
  const bar = () => {
    console.log(this);
  };
  return bar;
}

new Person(); //logs PersonĀ {}
Person(); //logs the window object

Now you know that arrow functions take this from the lexical scope of where they are evaluated. In first case, bar is run with this equal to Person, and in second case the window object.

Related