Stage 3 Draft / October 7, 2026

Disallow Adding New Private Fields to Non-extensible Objects

7.3 Operations on Objects

7.3.27 PrivateFieldAdd ( O, P, value )

The abstract operation PrivateFieldAdd takes arguments O (an Object), P (a Private Name), and value (an ECMAScript language value) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. If the host is a web browser, then
    1. Perform ? HostEnsureCanAddPrivateElement(O).
  2. If ? IsExtensible(O) is false, throw a TypeError exception.
  3. Let entry be PrivateElementFind(O, P).
  4. If entry is not empty, throw a TypeError exception.
  5. Append PrivateElement { [[Key]]: P, [[Kind]]: field, [[Value]]: value } to O.[[PrivateElements]].
  6. Return unused.

7.3.28 PrivateMethodOrAccessorAdd ( O, method )

The abstract operation PrivateMethodOrAccessorAdd takes arguments O (an Object) and method (a PrivateElement) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. Assert: method.[[Kind]] is either method or accessor.
  2. If the host is a web browser, then
    1. Perform ? HostEnsureCanAddPrivateElement(O).
  3. Let entry be PrivateElementFind(O, method.[[Key]]).
  4. If entry is not empty, throw a TypeError exception.
  5. Append method to O.[[PrivateElements]].
  6. Return unused.
Note

The values for private methods and accessors are shared across instances. This operation does not create a new copy of the method or accessor.

10.2 ECMAScript Function Objects

10.2.1.3 InitializeInstanceElements ( obj, ctor )

The abstract operation InitializeInstanceElements takes arguments obj (an Object) and ctor (an ECMAScript function object or a built-in function object) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. Let methods be ctor.[[PrivateMethods]].
  2. If methods is not empty, then
    1. If the host is a web browser, then
      1. Perform ? HostEnsureCanAddPrivateElement(obj).
    2. If ? IsExtensible(obj) is false, throw a TypeError exception.
  3. For each PrivateElement method of methods, do
    1. Perform ? PrivateMethodOrAccessorAdd(obj, method).
  4. Let fields be ctor.[[Fields]].
  5. For each element fieldRecord of fields, do
    1. Perform ? DefineField(obj, fieldRecord).
  6. Return unused.

15.7 Class Definitions

15.7.14 Runtime Semantics: ClassDefinitionEvaluation

The syntax-directed operation ClassDefinitionEvaluation takes arguments classBinding (a String or undefined), className (a property key or a Private Name), and sourceText (ECMAScript source text) and returns either a normal completion containing a function object or an abrupt completion.

Note

For ease of specification, private methods and accessors are included alongside private fields in the [[PrivateElements]] slot of class instances. However, any given object has either all or none of the private methods and accessors defined by a given class. This feature has been designed so that implementations may choose to implement private methods and accessors using a strategy which does not require tracking each method or accessor individually.

For example, an implementation could directly associate instance private methods with their corresponding Private Name and track, for each object, which class constructors have run with that object as their this value. Looking up an instance private method on an object then consists of checking that the class constructor which defines the method has been used to initialize the object, then returning the method associated with the Private Name.

This differs from private fields: because field initializers can throw during class instantiation, an individual object may have some proper subset of the private fields of a given class, and so private fields must in general be tracked individually.

It is defined piecewise over the following productions:

ClassTail : ClassHeritageopt { ClassBodyopt }
  1. Let envRecord be the LexicalEnvironment of the running execution context.
  2. Let classEnv be NewDeclarativeEnvironment(envRecord).
  3. If classBinding is not undefined, then
    1. Perform ! classEnv.CreateImmutableBinding(classBinding, true).
  4. Let outerPrivateEnv be the running execution context's PrivateEnvironment.
  5. Let classPrivateEnv be NewPrivateEnvironment(outerPrivateEnv).
  6. If ClassBody is present, then
    1. For each String description of the PrivateBoundIdentifiers of ClassBody, do
      1. If classPrivateEnv.[[Names]] contains a Private Name privateName such that privateName.[[Description]] is description, then
        1. Assert: This is only possible for getter/setter pairs.
      2. Else,
        1. Let name be a new Private Name whose [[Description]] is description.
        2. Append name to classPrivateEnv.[[Names]].
  7. If ClassHeritage is not present, then
    1. Let protoParent be %Object.prototype%.
    2. Let ctorParent be %Function.prototype%.
  8. Else,
    1. Set the running execution context's LexicalEnvironment to classEnv.
    2. NOTE: The running execution context's PrivateEnvironment is outerPrivateEnv when evaluating ClassHeritage.
    3. Let superclassRef be Completion(Evaluation of ClassHeritage).
    4. Set the running execution context's LexicalEnvironment to envRecord.
    5. Let superclass be ? GetValue(? superclassRef).
    6. If superclass is null, then
      1. Let protoParent be null.
      2. Let ctorParent be %Function.prototype%.
    7. Else if IsConstructor(superclass) is false, then
      1. Throw a TypeError exception.
    8. Else,
      1. Let protoParent be ? Get(superclass, "prototype").
      2. If protoParent is not an Object and protoParent is not null, throw a TypeError exception.
      3. Let ctorParent be superclass.
  9. Let proto be OrdinaryObjectCreate(protoParent).
  10. If ClassBody is not present, let ctor be empty.
  11. Else, let ctor be the ConstructorMethod of ClassBody.
  12. Set the running execution context's LexicalEnvironment to classEnv.
  13. Set the running execution context's PrivateEnvironment to classPrivateEnv.
  14. If ctor is empty, then
    1. Let defaultCtor be a new Abstract Closure with no parameters that captures nothing and performs the following steps when called:
      1. Let args be the List of arguments that was passed to this function by [[Call]] or [[Construct]].
      2. If NewTarget is undefined, throw a TypeError exception.
      3. Let ctorFunc be the active function object.
      4. If ctorFunc.[[ConstructorKind]] is derived, then
        1. NOTE: This branch behaves similarly to constructor(...args) { super(...args); }. The most notable distinction is that while the aforementioned ECMAScript source text observably calls the %Symbol.iterator% method on %Array.prototype%, this function does not.
        2. Let func be ! ctorFunc.[[GetPrototypeOf]]().
        3. If IsConstructor(func) is false, throw a TypeError exception.
        4. Let result be ? Construct(func, args, NewTarget).
      5. Else,
        1. NOTE: This branch behaves similarly to constructor() {}.
        2. Let result be ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%").
      6. Perform ? InitializeInstanceElements(result, ctorFunc).
      7. Return NormalCompletion(result).
    2. Let ctorFunc be CreateBuiltinFunction(defaultCtor, 0, className, « [[ConstructorKind]], [[SourceText]], [[PrivateMethods]], [[Fields]] », the current Realm Record, ctorParent).
  15. Else,
    1. Let ctorInfo be ! DefineMethod of ctor with arguments proto and ctorParent.
    2. Let ctorFunc be ctorInfo.[[Closure]].
    3. Perform MakeClassConstructor(ctorFunc).
    4. Perform SetFunctionName(ctorFunc, className).
  16. Set ctorFunc.[[SourceText]] to sourceText.
  17. Perform MakeConstructor(ctorFunc, false, proto).
  18. If ClassHeritage is present, set ctorFunc.[[ConstructorKind]] to derived.
  19. Perform ! DefineMethodProperty(proto, "constructor", ctorFunc, false).
  20. If ClassBody is not present, let classElements be a new empty List.
  21. Else, let classElements be the NonConstructorElements of ClassBody.
  22. Let instancePrivateMethods be a new empty List.
  23. Let staticPrivateMethods be a new empty List.
  24. Let instanceFields be a new empty List.
  25. Let staticElements be a new empty List.
  26. For each ClassElement classElement of classElements, do
    1. If IsStatic of classElement is false, then
      1. Let element be Completion(ClassElementEvaluation of classElement with argument proto).
    2. Else,
      1. Let element be Completion(ClassElementEvaluation of classElement with argument ctorFunc).
    3. If element is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to envRecord.
      2. Set the running execution context's PrivateEnvironment to outerPrivateEnv.
      3. Return ? element.
    4. Set element to ! element.
    5. If element is a PrivateElement, then
      1. Assert: element.[[Kind]] is either method or accessor.
      2. If IsStatic of classElement is false, let container be instancePrivateMethods.
      3. Else, let container be staticPrivateMethods.
      4. If container contains a PrivateElement existingElement such that existingElement.[[Key]] is element.[[Key]], then
        1. Assert: element.[[Kind]] and existingElement.[[Kind]] are both accessor.
        2. If element.[[Getter]] is undefined, then
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Getter]]: existingElement.[[Getter]], [[Setter]]: element.[[Setter]] }.
        3. Else,
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Getter]]: element.[[Getter]], [[Setter]]: existingElement.[[Setter]] }.
        4. Replace existingElement in container with combined.
      5. Else,
        1. Append element to container.
    6. Else if element is a ClassFieldDefinition Record, then
      1. If IsStatic of classElement is false, append element to instanceFields.
      2. Else, append element to staticElements.
    7. Else if element is a ClassStaticBlockDefinition Record, then
      1. Append element to staticElements.
    8. Else,
      1. Assert: element is empty.
  27. Set the running execution context's LexicalEnvironment to envRecord.
  28. If classBinding is not undefined, then
    1. Perform ! classEnv.InitializeBinding(classBinding, ctorFunc).
  29. Set ctorFunc.[[PrivateMethods]] to instancePrivateMethods.
  30. Set ctorFunc.[[Fields]] to instanceFields.
  31. Assert: ctorFunc.[[Extensible]] is true.
  32. NOTE: ctorFunc is freshly created and has not been exposed to user code, so it is always extensible when installing static private methods.
  33. For each PrivateElement method of staticPrivateMethods, do
    1. Perform ! PrivateMethodOrAccessorAdd(ctorFunc, method).
  34. For each element elementRecord of staticElements, do
    1. If elementRecord is a ClassFieldDefinition Record, then
      1. Let result be Completion(DefineField(ctorFunc, elementRecord)).
    2. Else,
      1. Assert: elementRecord is a ClassStaticBlockDefinition Record.
      2. Let result be Completion(Call(elementRecord.[[BodyFunction]], ctorFunc)).
    3. If result is an abrupt completion, then
      1. Set the running execution context's PrivateEnvironment to outerPrivateEnv.
      2. Return ? result.
  35. Set the running execution context's PrivateEnvironment to outerPrivateEnv.
  36. Return ctorFunc.

A Copyright & Software License

Copyright Notice

© 2026 Shu-yu Guo

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.