Babel replaces this with undefined

2020-07-02 08:37发布

问题:

This code

beforeEach(() => {
        this.asd= '123';
        this.sdf= '234';
        this.dfg= '345';
        this.fgh= '456';
});

has been transpiled by Babel to:

beforeEach(function() {
        undefined.asd= '123';
        undefined.sdf= '234';
        undefined.dfg= '345';
        undefined.fgh= '456';
});

Why?

回答1:

Presumably that code is at the top-level scope of a module, and so it's in strict mode (the default for modules is strict mode), or a file that is being evaluated in strict mode (because it has "use strict" or because of Babel's defaults).

The short version: If you're expecting this to be determined by beforeEach when calling your callback, you'll want to use a function function, not an arrow function. Read on for why Babel is transpiling as it is:

The fundamental thing about arrow functions (other than being concise) is that they inherit this from their context (like a variable they close over), instead of having it set by the caller. In strict mode, this at global scope is undefined. So Babel knows, at compile-time, that this within the arrow function will be undefined and optimizes it.

You've said in comments that this is inside another function, but my guess is that it's inside another arrow function, e.g.:

describe(() => {
    beforeEach(() => {
        this.asd= '123';
        // ...
    });
});

Since Babel knows that this is undefined in the describe callback, it also knows that this is undefined in the beforeEach callback.

If you put your code in a loose mode context, or inside a function call where this can't be determined at compile-time, it won't do that. For example, in strict mode your

beforeEach(() => {
  this.asd= '123';
  this.sdf= '234';
  this.dfg= '345';
  this.fgh= '456';
});

does indeed transpile to

'use strict';
beforeEach(function () {
  undefined.asd = '123';
  undefined.sdf = '234';
  undefined.dfg = '345';
  undefined.fgh = '456';
});

but this:

function foo() {
  beforeEach(() => {
    this.asd= '123';
    this.sdf= '234';
    this.dfg= '345';
    this.fgh= '456';
  });
}

transpiles to

'use strict';

function foo() {
  var _this = this;

  beforeEach(function () {
    _this.asd = '123';
    _this.sdf = '234';
    _this.dfg = '345';
    _this.fgh = '456';
  });
}

...because Babel doesn't know how foo will be called, and thus what this will be.



回答2:

I had this issue, and changed my fat arrow function into a normal function, and it seemed to work again.

() => {} 

changed to

function() {}


回答3:

Arrow functions don't have their own this or arguments binding. Instead, those identifiers are resolved in the lexical scope like any other variable. That means that inside an arrow function, this and arguments refer to the values of this and arguments in the environment the arrow function is defined in (i.e. "outside" the arrow function).
In your scenario, this at global scope is undefined. At compile-time, babel will transpile this within the arrow function as undefined.

If you are not very familiar with this, consider reading

  • MDN - this YDKJS
  • this & Object prototypes