Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

really confused by "enclosing scope" of javascript es6 arrow function

  • I did a lot research online, read many posts including MDN and so on.
  • I understand that for traditional defined functions, "this" within functions is defined by objects calling/invoking them (and several different cases, object literal, new constructor, event handler, etc.).
  • I understand that for arrow functions, "this" is defined lexically, by the enclosing context/scope, not by objects invoking them (though we can use a traditionally defined function (say, A) to wrap arrow functions (say, B) and thus pass the object reference to A first, and then to B)

Here come the questions:

  1. what exactly is an enclosing context? This is further convoluted since ES6 allows {} to be a block/scope/context. Are {} as delimiters good enough to define an "enclosing context" or it has to be within a function scope.

  2. One specific example:

     let EventEmitter = require('events').EventEmitter;
     class Person extends EventEmitter {
       constructor(name) {
         super();
         this.name = name;
       }
     }
     let mary = new Person('mary');
     mary.on('speak', (said) => {
       console.log(`${this.name}: ${said}`);
     });
     mary.emit('speak', 'you may delay, but time will not');
    

It simply setups a custom event and adds a callback function when the custom event is triggered. Why the arrow function here doesn't work?

"mary" is the object that calls the "on" function, which should set "this" within "on" to be "mary". The most important thing is, the arrow function is defined in "on" function in its parameter position (lexically enough, right?), why the arrow function can't get "this" value from its "enclosing context", that is, the "on" function here???

  1. the same example, with conventional function definition:

     let EventEmitter = require('events').EventEmitter;
     class Person extends EventEmitter {
       constructor(name) {
         super();
         this.name = name;
       }
     }
     let mary = new Person('mary');
     mary.on('speak', function(s) {
       console.log(this);
     });
     mary.emit('speak', 'you may delay, but time will not');
    

Now it works. I understand that given the old way of function definition, console.log(this) can now be dynamically bound to the object calling it. But wait, "mary" is the object and "on" is the immediate function being called. Shouldn't "on" form a closure for the anonymous function within it? And I remember "this" within the nested function cannot access "this" of its closure (the enclosing context, again, huh) and thus should not get the "mary" refernce. Why it works here?

  1. When we talk about something (say, A) within a function, does it mean A have to be in the {} of the function, or A could be in the parameter/argument area as well? That is, function(){A} vs. function(A){}.

  2. similarly, if an arrow function is passed as a parameter like function(()=>()) {}, does the outer function considered its enclosing scope? Or the enclosing scope in this case would be the outer of outer function?

The above might sound very stupid. Thanks a lot for your help.

like image 523
user3236895 Avatar asked Mar 17 '17 01:03

user3236895


People also ask

What can I use instead of arrow in JavaScript?

Since arrow functions don't have their own arguments , you cannot simply replace them with an arrow function. However, ES2015 introduces an alternative to using arguments : the rest parameter. // old function sum() { let args = []. slice.

What is enclosing scope in JavaScript?

Closures and nested scope However when a function is defined within another function the inner function has access to the variable scope of the outer function. This nesting of functions also results in a nesting of scope. The outer scope is said to “enclose” (hence the term closure) the scope of the inner function.

When should you not use arrow functions in ES6?

An arrow function doesn't have its own this value and the arguments object. Therefore, you should not use it as an event handler, a method of an object literal, a prototype method, or when you have a function that uses the arguments object.

What is the difference between a normal function and an ES6 arrow function?

Since regular functions are constructible, they can be called using the new keyword. However, the arrow functions are only callable and not constructible, i.e arrow functions can never be used as constructor functions. Hence, they can never be invoked with the new keyword.


1 Answers

I'm probably not using the word scope precisely here, but basically just think of a scope as a map of variable names to what locations in memory they refer to; a nested scope's name/variable pairs shadow (override) associations with the same name in the enclosing (aka parent) scope.

function foo() { // this is the "enclosing scope" of bar
  var a = 4    <-----------+
                           |
  var b = a // refers to --+

  function bar() {
    var a = 7    <-----------+
                             |
    var c = a // refers to --+
  }
}

this behaves exactly the same way as a does in the above example.

function scopes implicitly define a reference for this but ES2015 arrow function scopes and block scopes do not. Here is what those definitions would look like if they were explicit:

function foo() { // this is the enclosing scope of baz and the block below
  var this = ...  <-----------+--+
                              |  |
  var b = this // refers to --+  |
                                 |
  {                              |
    var q = this // refers to ---+
  }

  function bar() { // this is the enclosing scope of baz
    var this = ...  <-----------+--+
                                |  |
    var c = this // refers to --+  |
                                   |
    var baz = () => {              |
      var d = this // refers to ---+
    }
  }
}

The actual value at the memory location that this refers to in a particular scope is not lexically defined; it is set at runtime to the (memory location of the) object a function is called upon. But the shadowing of one reference by another is always defined lexically.

like image 103
Andy Avatar answered Oct 21 '22 04:10

Andy