Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Jan 25, 2022. It is now read-only.

Orthogonally-informed combination of public and private fields proposals

NotificationsYou must be signed in to change notification settings

tc39/proposal-class-fields

Repository files navigation

Daniel Ehrenberg, Jeff Morrison

Stage 4

A guiding example: Custom elements with classes

To define a counter widget which increments when clicked, you can define the following with ES2015:

classCounterextendsHTMLElement{clicked(){this.x++;window.requestAnimationFrame(this.render.bind(this));}constructor(){super();this.onclick=this.clicked.bind(this);this.x=0;}connectedCallback(){this.render();}render(){this.textContent=this.x.toString();}}window.customElements.define('num-counter',Counter);

Field declarations

With the ESnext field declarations proposal, the above example can be written as

classCounterextendsHTMLElement{x=0;clicked(){this.x++;window.requestAnimationFrame(this.render.bind(this));}constructor(){super();this.onclick=this.clicked.bind(this);}connectedCallback(){this.render();}render(){this.textContent=this.x.toString();}}window.customElements.define('num-counter',Counter);

In the above example, you can see a field declared with the syntaxx = 0. You can also declare a field without an initializer asx. By declaring fields up-front, class definitions become more self-documenting; instances go through fewer state transitions, as declared fields are always present.

Private fields

The above example has some implementation details exposed to the world that might be better kept internal. Using ESnext private fields and methods, the definition can be refined to:

classCounterextendsHTMLElement{  #x=0;clicked(){this.#x++;window.requestAnimationFrame(this.render.bind(this));}constructor(){super();this.onclick=this.clicked.bind(this);}connectedCallback(){this.render();}render(){this.textContent=this.#x.toString();}}window.customElements.define('num-counter',Counter);

To make fields private, just give them a name starting with#.

By defining things which are not visible outside of the class, ESnext provides stronger encapsulation, ensuring that your classes' users don't accidentally trip themselves up by depending on internals, which may change version to version.

Note that ESnext provides private fields only as declared up-front in a field declaration; private fields cannot be created later, ad-hoc, through assigning to them, the way that normal properties can.

Major design points

Public fields created with Object.defineProperty

A public field declarations define fields on instances with the internals ofObject.defineProperty (which we refer to in TC39 jargon as[[Define]] semantics), rather than withthis.field = value; (referred to as[[Set]] semantics). Here's an example of the impact:

classA{setx(value){console.log(value);}}classBextendsA{x=1;}

With the adopted semantics,new B() will result in an object which has a propertyx with the value1, and nothing will be written to the console. With the alternate[[Set]] semantics,1 would be written to the console, and attempts to access the property would lead to aTypeError (because the getter is missing).

The choice between[[Set]] and[[Define]] is a design decision contrasting different kinds of expectations of behavior: Expectations that the field will be created as a data property regardless of what the superclass contains, vs expectations that the setter would be called. Following a lengthy discussion, TC39 settled on[[Define]] semantics, finding that it's important to preserve the first expectation.

The decision to base public field semantics onObject.defineProperty was based on extensive discussion within TC39 and consultation with the developer community. Unfortunately,the community was rather split, while TC39 came down rather strongly on the side ofObject.defineProperty.

As a mitigation, thedecorators proposal provides the tools to write a decorator to make a public field declaration use[[Set]] semantics. Even if you disagree with the default, the other option is available. (This would be the case regardless of which default TC39 chose.)

Public fields areshipping in Chrome 72 with[[Define]] semantics, and this decision on semantics is unlikely to be revisited.

Fields without initializers are set toundefined

Both public and private field declarations create a field in the instance, whether or not there's an initializer present. If there's no initializer, the field is set toundefined. This differs a bit from certain transpiler implementations, which would just entirely ignore a field declaration which has no initializer.

For example, in the following example,new D would result in an object whosey property isundefined, not1.

classC{y=1;}classDextendsC{y;}

The semantics of setting fields without initializers toundefined as opposed to erasing them is that field declarations give a reliable basis to ensure that properties are present on objects that are created. This helps programmers keep objects in the same general state, which can make it easy to reason about and, sometimes, more optimizable in implementations.

Private syntax

Private fields are based on syntax using a#, both when declaring a field and when accessing it.

classX{  #foo;method(){console.log(this.#foo)}}

This syntax tries to be both terse and intuitive, although it's rather different from other programming languages. Seethe private syntax FAQ for discussion of alternatives considered and the constraints that led to this syntax.

There are no private computed property names:#foo is a private identifier, and#[foo] is a syntax error.

No backdoor to access private

Private fields provide a strong encapsulation boundary: It's impossible to access the private field from outside of the class, unless there is some explicit code to expose it (for example, providing a getter). This differs from JavaScript properties, which support various kinds of reflection and metaprogramming, and is instead analogous to mechanisms like closures andWeakMap, which don't provide access to their internals. Seethese FAQ entries for more information on the motivation for this decision.

Some mitigations which make it easier to access

  • Implementations' developer tools may provide access to private fields (V8 issue).
  • Thedecorators proposal gives tools for easy-to-use and controlled access to private fields.

Execution of initializer expressions

Public and private fields are each added to the instance in the order of their declarations, while the constructor is running. The initializer is newly evaluated for each class instance. Fields are added to the instance right after the initializer runs, and before evaluating the following initializer.

Scope: The instance under construction is in scope as thethis value inside the initializer expression.new.target is undefined, as in methods. References toarguments are an early error. Super method callssuper.method() are available within initializers, but super constructor callssuper() are a syntax error.await andyield are unavailable in initializers, even if the class is declared inside an async function/generator.

When field initializers are evaluated and fields are added to instances:

  • Base class: At the beginning of the constructor execution, even before parameter destructuring.
  • Derived class: Right aftersuper() returns. (The flexibility in howsuper() can be called has led many implementations to make a separate invisibleinitialize() method for this case.)

Ifsuper() is not called in a derived class, and instead some other public and private fields are not added to the instance, and initializers are not evaluated. For base classes, initializers are always evaluated, even if the constructor ends up returning something else. Thenew.initialize proposal would add a way to programmatically add fields to an instance which doesn't come fromsuper()/thethis value in the base class.

Specification

See thedraft specification for full details.

Status

Consensus in TC39

This proposal reachedStage 3 in July 2017. Since that time, there has been extensive thought and lengthy discussion about various alternatives, including:

In considering each proposal, TC39 delegates looked deeply into the motivation, JS developer feedback, and the implications on the future of the language design. In the end, this thought process and continued community engagement led to renewed consensus on the proposal in this repository. Based on that consensus, implementations are moving forward on this proposal.

Development history

This document proposes a combined vision forpublic fields andprivate fields, drawing on the earlierOrthogonal Classes andClass Evaluation Order proposals. It is written to be forward-compatible with the introduction of private methods and decorators, whose integration is explained in theunified class features proposal. Methods and accessors are defined ina follow-on proposal.

This proposal has been developed in this GitHub repository as well as in presentations and discussions inTC39 meetings. See the past presentations and discussion notes below.

DateSlidesNotes
July 2016Private State📝
January 2017Public and private class fields: Where we are and next steps📝
May 2017Class Fields Integrated Proposal📝
July 2017Unified Class Features: A vision of orthogonality📝
September 2017Class fields status update📝
November 2017Class fields, static and private📝
November 2017Class features proposals: Instance features to stage 3📝
November 2017ASI in class field declarations📝
May 2018Class fields: Stage 3 status update📝
September 2018Class fields and private methods: Stage 3 update📝
January 2019Private fields and methods refresher📝

Implementations

You can experiment with the class fields proposal using the following implementations:

Further implementations are on the way:

Activity welcome in this repository

You are encouraged to file issues and PRs this repository to

  • Ask questions about the proposal, how the syntax works, what the semantics mean, etc.
  • Discuss implementation and testing experience, and issues that arise out of that process.
  • Develop improved documentation, sample code, and other ways to introduce programmers at all levels to this feature.

If you have any additional ideas on how to improve JavaScript, see ecma262'sCONTRIBUTING.md for how to get involved.

About

Orthogonally-informed combination of public and private fields proposals

Resources

Code of conduct

Security policy

Stars

Watchers

Forks


[8]ページ先頭

©2009-2025 Movatter.jp