ALL > Computer and Education > courses > university courses > undergraduate courses > practice for Computer English Language > > 2017-2018-1-class > 2014329620093-孟培森 >
ES proposal: class fields Version 0
👤 Author: by x2014329620093 2018-01-15 11:48:02
The ECMAscript proposal “Class Fields” by Daniel Ehrenberg and Jeff Morrison is currently at stage 3. This blog post explains how it works.

Overview


Field declarations:
class MyClass {
instanceProperty = 0;
static staticProperty = 0;
}

Private fields have names starting with a # and are only visible within the body of the class:
class MyClass {
#foo; // must be declared
constructor(foo) {
this.#foo = foo;
}
incFoo() {
this.#foo++;
}
}

Declaring fields


With this proposal, objects now have two kinds of fields:

  • Properties, whose keys are strings or symbols.

  • Private fields that have names. More on private fields later.


Fields can be configured as follows:

  • Location of property:

    • Static: prefix static

    • Instance: no prefix



  • Visibility and name. A field can be either:

    • A public property with a fixed name

    • A public property with a computed key

    • A private field with a fixed name



  • Initializer: optional










































Location Visibility/name
foo; instance public
#foo; instance private
['f'+'oo']; instance computed
static foo; static public
static #foo; static private
static ['f'+'oo']; static computed

Initializers


With an initializer, you create a property and assign it a value at the same time. In the following code, = 0 is an initializer:
class MyClass {
x = 0;
y = 0;
}

This class is equivalent to:
class MyClass {
constructor() {
this.x = 0;
this.y = 0;
}
}

Initializers are executed before the constructor


class MyClass {
foo = console.log('initializer');
constructor() {
console.log('constructor');
}
}
new MyClass();
// Output:
// initializer
// constructor

Location of the field


Instance fields


Without any prefix, a declaration creates an instance field:
class MyClass {
foo = 123;
}
console.log(new MyClass().foo); // 123
console.log(Reflect.ownKeys(new MyClass()));
// ['foo']

Class fields


Declarations with the prefix static create fields for classes:
class MyClass {
static foo = 123;
}
console.log(MyClass.foo); // 123
console.log(Reflect.ownKeys(MyClass)); // ['foo']
// ['length', 'name', 'prototype', 'foo']

MyClass has the properties length and name, because it is also a function.

Private visibility


Kinds of privacy in javascript


In ES6 and later, you can already implement two kinds of privacy:

  • Soft privacy (that you can work around): put data into properties whose keys are symbols (details).
    const _counter = Symbol('counter');
    const _action = Symbol('action');

    class Countdown {
    constructor(counter, action) {
    this[_counter] = counter;
    this[_action] = action;
    }
    dec() {
    if (this[_counter] < 1) return;
    this[_counter]--;
    if (this[_counter] === 0) {
    this[_action]();
    }
    }
    }


  • Hard privacy (that is mostly safe; for complete safety, you have to take additional precautions): data is in the values of WeakMaps whose keys are the objects that the private data is associated with (details).
    const _counter = new WeakMap();
    const _action = new WeakMap();
    class Countdown {
    constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
    }
    dec() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
    _action.get(this)();
    }
    }
    }



Private fields are basically a more convenient way of doing hard privacy.

From underscores to private fields


Another common technique is to indicate which properties are considered private by prefixing their names with underscores:
class Countdown {
constructor(counter, action) {
this._counter = counter;
this._action = action;
}
dec() {
if (this._counter < 1) return;
this._counter--;
if (this._counter === 0) {
this._action();
}
}
}

This technique doesn’t give you any protection, but it is more convenient than using symbols or WeakMaps.

Such code can be changed to use the new private field feature in two steps:

  1. Replace each underscore with a hash symbol.

  2. Declare all private fields at the beginning of the class.


class Countdown {
#counter;
#action;

constructor(counter, action) {
this.#counter = counter;
this.#action = action;
}
dec() {
if (this.#counter < 1) return;
this.#counter--;
if (this.#counter === 0) {
this.#action();
}
}
}

Countdown does not have any instance properties:
const countdown = new Countdown(5, () => {});
Reflect.ownKeys(countdown); // []

Private fields are similar to hard privacy via WeakMaps


In the spec, private fields are managed via a data structure that is attached to objects. That is, private fields are roughly handled as follows.
{
const _counter = Symbol();
const _action = Symbol();

class Countdown {

__PrivateFieldValues__ = {
[_counter]: undefined,
[_action]: undefined,
};

constructor(counter, action) {
this.__PrivateFieldValues__[_counter] = counter;
this.__PrivateFieldValues__[_action] = action;
}

···
}
}

A consequence of this approach is that you can only access private fields if you are inside the body of a class; access to this does not give you access to private data. In other words, you need to know the right symbol to access the data (__PrivateFieldValues__ in the example is not fully protected, but the corresponding data structure in the spec is).

More information in the spec: Sect. “Private Names and references

Not yet supported


Two elements of classes cannot yet be private:

  • Method definitions

  • Setters and getters


An upcoming proposal that fills this gap is currently at stage 2.

Please login to reply. Login

Reversion History

Loading...
No reversions found.