176

I'm using babel6 and for my pet project I'm creating a wrapper for XMLHttpRequest, for the methods I can use:

open = (method, url, something) => {
  return this.xhr.open(method, url, something);
}

but for the properties arrow function doesn't work

this works:

get status() { return this.xhr.status; }

but I can not use

get status = () => this.xhr.status;

Is this intentional?

Gabor Dolla
  • 2,680
  • 4
  • 14
  • 13
  • You don't need the curly brackets or the return; you can just say `(method, url, something) => this.xhr.open(method. url, something)`. –  Nov 20 '15 at 14:06
  • `get` is a part of an object literal or a class definition, a variable assignment is not. Why do you think they should work alike? – Bergi Nov 20 '15 at 14:22
  • 5
    ```status => this.xhr.status``` (c# 7 syntaxe) or maybe ```get status() => this.xhr.status```would indeed have been a great syntaxic sugar for readability but Javascript not Typescript doesn't (yet?) support it – Charles HETIER May 28 '19 at 15:01
  • I need this so much in my life!!! – Cristian E. Aug 30 '21 at 12:34

3 Answers3

182

According to the ES2015 grammar, a property on an object literal can only be one of three things:

PropertyDefinition:

  • IdentifierReference
  • PropertyName : AssignmentExpression
  • MethodDefinition

The only one of these type that allows a leading get is MethodDefinition:

MethodDefinition :

  • PropertyName ( StrictFormalParameters ) { FunctionBody }
  • GeneratorMethod
  • get PropertyName ( ) { FunctionBody }
  • set PropertyName ( PropertySetParameterList ) { FunctionBody }

As you can see, the get form follows a very limited grammar that must be of the form

get NAME () { BODY }

The grammar does not allow functions of the form get NAME = ....

FreeAsInBeer
  • 12,937
  • 5
  • 50
  • 82
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • Thanks for your help, I accept your answer. Do you know where it's defined that getter/setter can not be used with an assignment ? Just curious. – Gabor Dolla Nov 20 '15 at 13:20
  • @GaborDolla Edited to refer to the object literal grammar in the ECMAScript spec. – apsillers Nov 20 '15 at 13:31
70

The accepted answer is great. It's the best if you're willing to use normal function syntax/ object initialization/MethodDefinition syntax like get NAME () { BODY } instead of compact "arrow function syntax".

But maybe you really like arrow functions; maybe you use the arrow function for another reason which a normal function syntax cannot replace; you may need a different solution.

For example, I notice OP uses this, you may want to bind this lexically; aka "non-binding of this"), and arrow functions are good for that lexical binding.

You can still use an arrow function with a getter via the Object.defineProperty technique, specifically with the accessor descriptor approach:

{
  ...
  Object.defineProperty(your_obj, 'status', { 
     get : () => this.xhr.status 
  });
  ...
}

See mentions of object initialization technique (aka get NAME() {...}) vs the defineProperty technique (aka get : ()=>{}). There is at least one significant difference, using defineProperty requires the variables already exists:

Defining a getter on existing objects

i.e. with Object.defineProperty you must ensure that your_obj (in my example) exists and is saved into a variable (whereas with a object-initialization you could return an object-literal in your object initialization: {..., get(){ }, ... }). More info on Object.defineProperty specifically, here

An advantage of the Object.defineProperty(...) approach is that you can run the code in a loop (i.e. define multiple properties)

Another difference between the two approaches is when you use get on an class vs defineProperty on an instance of a class:

When using get the property will be defined on the instance's prototype, while using Object.defineProperty() the property will be defined on the instance it is applied

Object.defineProperty(...) seems to have comparable browser support to the get NAME(){...} syntax; modern browsers, IE 9.

Notice the other options available to you when using Object.defineProperty(...):

  • enumerable: set to true if you want to enumerate/iterate over your new property using Object.keys
  • configurable : set to true if you want to redefine or delete your property
Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
  • 19
    Clever, but it's ultimately much more verbose than just: `get status() { return this.xhr.status; }` – devuxer Feb 24 '18 at 01:23
  • 8
    @devuxer I agree it's too verbose. But just to be clear, your `this` *must* be the object in which your `get status() { ... }` is defined. But *my* `this` *could* be something else, due to lexical binding differences, right? – Nate Anderson Feb 24 '18 at 17:54
  • 3
    Agree...though in practice, I haven't run into a case where `this` isn't what I want in a get accessor. (The `this` binding benefits of arrow functions seem to come into play when passing functions around, as with event handlers and callbacks.) – devuxer Feb 26 '18 at 20:37
  • 4
    I agree, I frequently use fat arrow + lexical bindings `()=>{}` for the callbacks I pass to a **Promise**, like `$http(...).then((promise_result)=> this...}))`. If I don't use fat-arrow, `this` will represent the global `Window` object; not very useful. But I seldom (never?) have used `()=>{}` as the function for a "get accessor" as you say... at least `this` inside of `get()` will represent the object on which `get()` is defined (which is already more useful than `Window`; so there's no need to use a fat-arrow function!) – Nate Anderson Feb 26 '18 at 20:48
  • 1
    The `defineProperty` approach is useful in loops. Right now I just used it to expose some properties of a chiled object from the containing one. – Edurne Pascual Oct 17 '20 at 11:24
  • 1
    Very useful. I used this approach to define some field of an object that was depending on values on the containing object (a class object more precisely). So I had to use `this` as a reference to the class object which declared the variable. However for unknown reasons these properties where not returned by `Object.keys(my_object)` – Bemipefe Nov 21 '20 at 17:46
  • 1
    @Bemipefe, Object.keys will list your property if you define the property like this; notice we explicitly set the value of the `enumerable` argument to **true** (if omitted, [`enumerable` defaults to **false**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Description)) `my_object = {}; Object.defineProperty(my_object, 'prop', { get: ()=>'val', enumerable: true }); Object.keys(my_object);` – Nate Anderson Nov 23 '20 at 02:33
-1

Yes, it is intentional that you cannot use arrow functions for properties in JavaScript. Arrow functions do not have their own bindings to this, so they cannot be used to access properties on the object they are defined on.

In your example, the status property is defined on the XMLHttpRequest object. If you try to use an arrow function to define the status property, the arrow function will not have a this binding to the XMLHttpRequest object, so it will not be able to access the status property.

To define a property using an arrow function, you can use the Object.defineProperty() method. For example, the following code defines a status property using an arrow function:

const xhr = new XMLHttpRequest();
Object.defineProperty(xhr, 'status', {
  get: () => this.xhr.status,
});

This code uses the Object.defineProperty() method to define a property called status on the xhr object. The get property of the status property is an arrow function that returns the value of the status property on the XMLHttpRequest object.

I hope this helps! Let me know if you have any other questions.