Original link:

www.codementor.io/dariogarcia…

This is the second article on understanding Javascript “This” to take a closer look at the arrow function.

I made the same mistake when I used the arrow function. At that time, I was not clear about the direction of this and the fact that the arrow function does not have its own this.

This article can serve as a second article on understanding “This” in JavaScript.

We’ll go through the same example, but we’ll use the arrow function instead of the normal one to compare the output.

The motivation for the second article is that while arrow functions are a powerful new feature in ES6, they must not be abused.

Default context for “this”

Arrow functions do not bind their own this; instead, they inherit from the parent scope, known as “lexical scope (static scope).” This makes the arrow function the best choice in some scenarios, but also the worst.

The translator’s note:

Lexical scoping: lexical scope/static scope

The scope of a function is determined when the function is defined.

The opposite is dynamic scope: the scope of a function is determined at the time the function is called.

Here’s an example:

var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar();
// The result is...?
Copy the code

Assuming that JavaScript is static scoped, the analysis is as follows:

If foo is executed, it first looks inside foo to see if the variable value exists.

If not, we look for the previous layer of code based on where it was written, and we find that value is equal to 1, so the result is printed as 1.

Assuming that JavaScript uses dynamic scope, the analysis process is as follows:

When we execute foo, we still look inside foo to see if there is a local variable value.

If not, the value variable is looked up from within the scope of the calling function, that is, the bar function, so the result is printed as 2.

JavaScript is static scoped, so the result of this example is 1.

Let’s look at the first example rewritten as an arrow function:

// define a function
const myFunction = () => {
  console.log(this);
};

// call it
myFunction();
Copy the code

What can we expect this to be? . Yeah, it’s just like a normal function, window/ global object. The same effect but not the same cause. Under normal functions, the scope is bound globally by default; But as I said earlier, arrow functions don’t have their own this, but they inherit from the parent scope, which in this case is the global scope.

What if we use strict? There is no difference, the result will be the same, because the scope comes from the parent.

Arrow functions as methods

const myObject = { myMethod: () => { console.log(this); }};Copy the code

What about now?

In this case, some people would say, well, it depends on who’s calling the method, just like a normal function. But that’s not the case. Let’s take a look…

myObject.myMethod() // this === window or global object

const myMethod = myObject.myMethod;
myMethod() // this === window or global object
Copy the code

Weird, right? Remember: arrow functions do not bind their own scopes, but inherit from their parent. In this case, the Window/global object.

Let’s change that a little bit.

const myObject = {
  myArrowFunction: null,
  myMethod: function() { this.myArrowFunction = () => { console.log(this) }; }};Copy the code

We need to call myObject.myMethod() to initialize myObject.myArrowFunction, and let’s see what the output looks like.

myObject.myMethod() // this === myObject

myObject.myArrowFunction() // this === myObject

const myArrowFunction = myObject.myArrowFunction;
myArrowFunction() // this === myObject
Copy the code

Is that clear now? When we call myObject.mymethod (), we initialize myObject.myArrowFunction with the arrow function inside myMethod, so it will inherit its scope. We can clearly see a perfect example: closures.

Explicit, Hard, and New bindings

What happens if we try to bind scopes by these means?

Let’s see…

const myMethod = () => {
  console.log(this);
};

const myObject = {};
Copy the code

Explicitly bound

myMethod.call(myObject, args1, args2, ...) // this === window or global object
myMethod.apply(myObject, [array of args]) // this === window or global object
Copy the code

Hard binding

const myMethodBound = myMethod.bind(myObject);
myMethodBound(); // this === window or global object
Copy the code

The New binding

new myMethod(); // Uncaught TypeError: myMethod is not a constructor
Copy the code

As you can see, no matter which way we tried to bind the scope, it didn’t work. Similarly, arrow functions have no constructors, so you can’t use new.

API callback

This part is very interesting. Arrow functions are a great choice for interface callbacks (asynchronous code), and only if we use closures, let’s look at the following code…

myObject = {
  myMethod: function () {
    helperObject.doSomethingAsync('superCool', () => { console.log(this); // this === myObject }); }};Copy the code

This is a perfect example of where we need to do something in an asynchronous process, and we wait for some result to perform some action, but we don’t need to worry about the scope that we’re operating on.

But what if we need to extract and refactor some code for reuse? Such as:

const reusabledCallback = () => {
  console.log(this); // this === window or global object
};

myObject = {
  myMethod: function () {
    helperObject.doSomethingAsync('superCool', reusabledCallback); }};Copy the code

If we did, we would break the currently working code, and remember: no matter how we tried to bind the scope, it would fail. So if you decide to do this, you must use normal functions and manually bind scopes, for example:

const reusabledCallback = function () {
  console.log(this);
};

myObject = {
  myMethod: function () {
    helperObject.doSomethingAsync('superCool', reusabledCallback.bind(myObject)); }};Copy the code

conclusion

Arrow functions are a powerful new feature in ES6, but we must be careful and judicious when using them. I keep looking for places where the arrow function doesn’t work, and it can make some errors hard to track down, especially if we don’t understand how it works.

My advice is that in the case of closures or callbacks, arrow functions are a good choice. But in class/object, constructor methods, it is not a good choice.

PS:

Arrow functions have many more interesting features such as: arguments or Prototype, but this article focuses on the scope of this. More information can be found in this document ** Mozilla Web Docs **.


If you have any questions or comments, please leave me a comment!