Difference between adding a method to a class with

2019-05-05 04:47发布

I have recently come across two ways of adding a method to a class in javascript ES6. Here they are in a nutshell:

class SomeClass {

  someMethod(arg) {
    console.log(this.anotherMethod); 
      // This will produce an error because 'this' is undefined here.
  }

  anotherMethod = (arg) => {
    // does stuff
  }

}

Can someone give me a good explanation of what is happening here, and the meaning of the syntax? I understand from the babel tutorial that the arrow => in ES6 indicates a function. But something else is going on here I think.

Specifically, if I'm not mistaken, if I were try to access someMethod() via the this keyword, it would not work. I think the key is in an explanation that I can't quite parse form the Babelify ES6 tutorial:

Unlike functions, arrows share the same lexical this as their surrounding code.

Does this mean that the arrow symbol will assign the function to the this of the scope surrounding it? So that if you use an arrow in the scope of a class, the function is being assigned to the object to which this points?

I ran this the sample code above through Babelify's ES6 repl. I can't quite follow the code. It looks to me as if the method created using the arrow syntax is being added to the new Class=Object itself, whereas the method created without the arrow syntax is being added to the Class=Object's prototype.

Does anyone have a good concise explanation of what the difference is, and what the code below is doing?

"use strict";

var _createClass = (function() {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };
})();

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

var SomeClass = (function () {
  function SomeClass() {
    _classCallCheck(this, SomeClass);

    this.anotherMethod = function (dog) {
      // do stuff
    };
  }

  _createClass(SomeClass, [{
    key: "someMethod",
    value: function someMethod(arg) {
      console.log(this.anotherMethod);
      // This will produce an error b/c this is undefined here.
    }
  }]);

  return SomeClass;
})();

3条回答
不美不萌又怎样
2楼-- · 2019-05-05 05:22

Can someone give me a good explanation of what is happening here, and the meaning of the syntax?

Your understanding and expectation of arrow functions is correct.

You are using a controversial ES7 proposal: class properties. It is basically the same as declaring that property inside the constructor:

constructor() {
  this.anotherMethod = (arg) => {
    // does stuff
  };
}

Hence this would refer to the instance.

If you disable experimental mode in Babel you will see that this property declaration produces a syntax error.

查看更多
时光不老,我们不散
3楼-- · 2019-05-05 05:24

The difference is that one is valid and the other is a syntax error.

According to Class Definitions,

ClassDeclaration[Yield, Default] :
    class BindingIdentifier[?Yield] ClassTail[?Yield]
    [+Default] class ClassTail[?Yield]

ClassTail[Yield] :
    ClassHeritage[?Yield]opt { ClassBody[?Yield]opt }

ClassBody[Yield] :
    ClassElementList[?Yield]

ClassElementList[Yield] :
    ClassElement[?Yield]
    ClassElementList[?Yield] ClassElement[?Yield]

ClassElement[Yield] :
    MethodDefinition[?Yield]
    static MethodDefinition[?Yield]
    ;

That means that a class body can only contain method definitions or static method definitions.

They are defined in Method Definitions:

MethodDefinition[Yield] :
    PropertyName[?Yield] ( StrictFormalParameters ) { FunctionBody }
    GeneratorMethod[?Yield]
    get PropertyName[?Yield] ( ) { FunctionBody }
    set PropertyName[?Yield] ( PropertySetParameterList ) { FunctionBody }

Therefore, the following code is not allowed inside a class body:

anotherMethod = (arg) => {
  // does stuff
}

If you use Traceur instead of Babel, it will complain about the syntax error.

查看更多
▲ chillily
4楼-- · 2019-05-05 05:40

Does this mean that the arrow symbol will assign the function to the this of the scope surrounding it?

Yes, though I'd have said "the this of the current execution context".

If I understand what you're asking correctly, the example can be simpler:

// When called with new, this is a new object
function Foo(name) {
  this.name = name;
}

// Get this of the current execution context
var currentThis = this;

// Using an arrow function means that when the method is called
// as a method of a Foo instance, this will be
// the current this
Foo.prototype.bar = (x)=> {
  console.log(x + ':' + (typeof this));
  return this;
};

var foo = new Foo('foo');

console.log(foo.name); // fo
console.log(foo.bar('bar') == currentThis); // bar:object
                                             // true

Sorry, missed:

Does anyone have a good concise explanation of what … the code below is doing?

It's a round–about way of creating a constructor. The _createClass function copies properties from the protoProps object to the constructor's prototype, and staticProps object to the constructor itself using Object.definePropety.

The constructor ("class") also has a _classCallCheck method so if, when called, its this isn't an instance of itself (i.e. it hasn't been called as a constructor), then the call fails. This is to check that it's been called with new, but it's not foolproof since this can be set in other ways (though I don't know if there's any point to that in this case).

Maybe that cuts a few corners, but it's the gist of it.

查看更多
登录 后发表回答