11
class a {
    get b() {
        delete this.b;
        return this.b = 1;
    }
}

var c = {
    get b() {
        delete this.b;
        return this.b = 1;
    }
}

console.log(c.b); // works as expected
console.log((new a()).b); // throws error

The above code should work fine but the last line throws.

Uncaught TypeError: Cannot set property b of # which has only a getter(…)

Clearly the getter is not being deleted in class whereas it works fine in object. I am on latest stable chrome.

Lazy Getter MDN Entry

Achshar
  • 5,153
  • 8
  • 40
  • 70
  • You don't have a constructor in `class a` – Redu Jun 22 '16 at 20:56
  • @Redu Constructor is optional in js classes. – Achshar Jun 22 '16 at 21:00
  • I could not find a reference after a fair amount of googling, so I'm making this a comment instead of an answer -- but I am 95% certain that methods in classes end up being non-configurable, and hence, delete fails. You can mimic that behavior in "regular" classes. – Jeremy J Starcher Jun 22 '16 at 21:03
  • @JeremyJStarcher So it's by design immutable. Cool. I can accept that if you add it. – Achshar Jun 22 '16 at 21:04

3 Answers3

21

The getter of the class sits on the .prototype object, not on this, that's why your attempt to delete it fails (and, as Jeremy points out, it is not deletable).

You can however simply create an own property on the instance that shadows the getter:

class a {
    get b() {
        Object.defineProperty(this, "b", { value: 1, writable: false, configurable: true })
        return this.b;
    }
}

var c = new a;
console.log(c.b); // 1

We have to use Object.defineProperty() as a simple assignment would find the inherited property that has no setter and throws.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 3
    Oh.. shadowing a getter -- that idea never crossed my mind. That's cool. – Jeremy J Starcher Jun 22 '16 at 21:44
  • You are just creating the `b` property of `a` i don't think you need all that ceremony. Just do `get b() { return this.b } set b(v) {this.b = v}` and it's all fine. – Redu Jun 22 '16 at 21:45
  • @Redu: That's infinite recursion? – Bergi Jun 22 '16 at 21:48
  • I just did `class aa { get b() {return this.b;} set b(v) {this.b = v} }` and then `zz = new aa();` then `aa.b = 100;` then `aa.b;` returns `100` as expected. – Redu Jun 22 '16 at 21:51
  • 2
    @Redu: Try `zz.b = 100` and you'll get a *Uncaught RangeError: Maximum call stack size exceeded* – Bergi Jun 22 '16 at 21:52
  • Yes sorry... well my bad but it comes back to answer... `zz.hasOwnProperty("b"); // false` `"b" in zz; // true` so `b is in the prototype chain. So the getter and setter may access `b` through `this.constructor.prototype.b` – Redu Jun 22 '16 at 21:59
  • @Redu: I'm not sure what you mean – Bergi Jun 22 '16 at 22:07
  • @Bergi You are a genius! – Alex Apr 12 '18 at 15:43
  • Might be worth explaining why this will memoize the property (or at least point out inheritance). – Rax Adaam Jan 20 '23 at 17:35
0

By design, class properties are not deletable -- reference: https://github.com/jeffmo/es-class-fields-and-static-properties

Instance Field Initialization Process

The process for executing a field initializer happens at class instantiation time. The following describes the process for initializing each class field initializer (intended to run once for each field in the order they are declared):

  • Let instance be the object being instantiated.
  • Let fieldName be the name for the current field (as stored in the slot on the constructor function).
  • Let fieldInitializerExpression be the thunked initializer expression for the current field (as stored in the slot on the constructor function).
  • Let initializerResult be the result of evaluating fieldInitializerExpression with this equal to instance.
  • Let propertyDescriptor be PropertyDescriptor{[[Value]]: initializerResult, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false}. Call instance.[[DefineOwnProperty]](fieldName, propertyDescriptor).
Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
  • 1
    Nothing in this question is about the proposed ES class properties syntax. – loganfsmyth Jun 22 '16 at 22:46
  • That is incorrect, the spec [link](https://www.ecma-international.org/ecma-262/6.0/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation) and all major browsers they have configurable:true and enumerable:false. For properties on object literals the enumerable is true – Panos Theof Jul 24 '17 at 11:58
0

Well obviously this is about constructors. When you neglect the constructor the property you define with the getter gets created at the constructors prototype hence the correct way of doing the job probably should be like as follows;

class a {
    get b() {
        delete this.constructor.prototype.b;
        return this.constructor.prototype.b = 1;
    }
}
z = new a();
z.b; // 1
Redu
  • 25,060
  • 6
  • 56
  • 76
  • 1
    Referencing `this.constructor.prototype` is usually a bad idea when it comes to subclassing. Just explicitly reference `a.prototype` if that is what you mean. – Bergi Jun 22 '16 at 21:49
  • In what environment are you executing this in? As Jeremy points out above, this should throw in ES6. – Bergi Jun 22 '16 at 21:51