How to change what an ES6 arrow function's 

2019-03-01 07:14发布

In the traverse npm package there's this example

var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];

traverse(obj).forEach(function (x) {
    if (x < 0) this.update(x + 128);
});

Inside the callback function you can call this.update. I know in these kinds of cases you should use the regular (not ES6 arrow) style of function definitions like shown above.

But out of curiosity, how would you make that code work with ES6 arrow function syntax? If I try as follows I get TypeError: Cannot read property 'update' of undefined because of course this is not the same as above.

traverse(obj).forEach((x) => {
    if (x < 0) this.update(x + 128);
});

I tried to change this by using bind but wasn't successful. How do you change this in ES6 arrow functions?

2条回答
神经病院院长
2楼-- · 2019-03-01 07:46

How do you change this in ES6 arrow functions?

You can't, that's the whole point of using arrow functions (besides concise syntax). If you need to control this, don't use an arrow function.

查看更多
【Aperson】
3楼-- · 2019-03-01 07:59

Arrow functions in ES6 have lexical this. This means you can always tell their this-value by asking the question "What was the value of this when the function was defined?" They are immune to the this-altering effects of call, apply, bind, and method invocation.

Let's take a look at the intended effect by compiling ES6 to ES5 with Babel.

ES6:

let foo = {
  bar() {
    return [
      () => this,
      function() { 
        return this 
      }
    ]
  },
  baz: () => this
}

let fns = foo.bar()
fns.push(...fns.map((fn, i) => fn.bind({i})))
fns.map(fn => fn()).forEach(thisVal => {
  console.log(thisVal)
  console.log('-------')
})
console.log(foo.baz())

Compiled to ES5:

'use strict';

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var foo = {
  bar: function bar() {
    var _this = this;

    return [function () {
      return _this;
    }, function () {
      return this;
    }];
  },

  baz: function baz() {
    return undefined;
  }
};

var fns = foo.bar();
fns.push.apply(fns, _toConsumableArray(fns.map(function (fn, i) {
  return fn.bind({ i: i });
})));
fns.map(function (fn) {
  return fn();
}).forEach(function (thisVal) {
  console.log(thisVal);
  console.log('-------');
});
console.log(foo.baz());

Check out what happened inside foo.bar. There was an inserted line:

var _this = this;

The arrow function's body was changed so that it returned _this from its lexical outer scope. Therefore, binding the value of this is not supposed to have any effect. By "freezing" the value of "this" just before the function definition, we have prevented the ability to change the function's behavior through binding, using call/apply, or calling the function as a method.

Notice how baz's this value was simply replaced with undefined. That's because if we ask the question "What was the value of this when the function was defined?", we obviously must answer either undefined or some global object context, like Node's global or the browser's window. By asking this question, you can easily deduce the value of this inside arrow functions, and that is a big part of their inherent value.

The output is therefore:

{ bar: [Function: bar], baz: [Function: baz] }
-------
undefined
-------
{ bar: [Function: bar], baz: [Function: baz] }
-------
{ i: 1 }
-------
undefined

The first and third outputs correspond to the arrow function defined within bar(). Notice how they automatically have a this-value that points to the object referred to by foo because they were created with the foo.bar() call. Useful right? Only the behavior of the non-arrow function could be changed by binding. Before binding, it's this-value was undefined, a common cause of many JS bugs and redundant bindings when passing around functions. Arrow functions take away a lot of that hassle.

查看更多
登录 后发表回答