I've attempted to sum up everything we have decided about object
initializers (aka object literals).  A short draft spec is included.
Comments welcome from everyone, especially from ES4 WG members who might
remember about things I've forgotten, or correct misunderstandings.

--lars
Title: Object literals

Object initializer syntax


NAME:                       "Object initializer syntax"
FILE:                       spec/language/object-literals.html
CATEGORY:                   Expressions (E262-3 chapter 11)
SOURCES:                    ES3; REFERENCES [1]-[6]
SPEC AUTHOR:                Lars
DRAFT STATUS:               DRAFT 1 - 2008-03-20
REVIEWED AGAINST ES3:       YES
REVIEWED AGAINST ERRATA:    YES
REVIEWED AGAINST BASE DOC:  YES
REVIEWED AGAINST PROPOSALS: YES
REVIEWED AGAINST CODE:      NO
REVIEWED AGAINST TICKETS:   YES
IMPLEMENTATION STATUS:      ?
TEST CASE STATUS:           ?


REFERENCES

[1] ES4 base document
[2] Ticket #164
[3] Ticket #165
[4] Ticket #219
[5] Ticket #319
[6] Bug fixes proposal, item about comma at the end of the field list

Synopsis

This note tries to pin down all that has been decided about object initializer syntax. A brief rationale is attached at the end.

Primary syntax

In its general form an object initializer is comprised of a brace-delimited comma-separated list of fields with the last field optionally followed by a comma, followed by an optional type annotation (which has to be a structural record type).

    ObjInit ::= "{" ( ( Field "," )* Field ","? )? "}" [ ":" RecType ]
    Field   ::= FieldName ":" AssignmentExpression
              | "var" FieldName ":" AssignmentExpression
              | "const" FieldName ":" AssignmentExpression
              | "get" FieldName "(" ")" [":" Type] FunctionBody
              | "set" FieldName "(" Param ")" [ ":" "void" ] FunctionBody
    FieldName ::= AnyIdentifier | AnyIdentifier "::" AnyIdentifier | LiteralString | LiteralNumber
    AnyIdentifier ::= Identifier | ReservedWord

The FunctionBody of a getter can be a block or an _expression_. The FunctionBody of a setter can only be a block (until we decide that ": void" on an _expression_ closure transforms the value computed to void before "returning").

It is possible to have a getter without a setter and a setter without a getter. A compatible getter or setter will be generated for the missing method. The generated setter method receives a value and discards it silently (this corresponds with the view that writing to ReadOnly properties fails silently). The generated getter method throws a ReferenceError.

Fields names may be repeated only if none of the instances of a name are qualified by var, const, get, or set.

Construction

Unlike the case in ES3, the program can't shadow the binding for Object in order to invoke an alternative object constructor for object initializers.

Secondary syntax

Suppose T is a structural record type:

    type T = { x: int, y: double }

Then the new operator can be used as follows:

    new T(10, 2.5)

The meaning of this is precisely:

    { x: 10, y: 2.5 } : T

There must be as many arguments to new as there are fields in T. The initializers are matched with fields by the order in which they appear.

Semantics of subphrases

Types and fixtures

If a property name in the record type that annotates the literal matches a field name in the literal then the field is a fixture (as opposed to a dynamic property) and the type of the fixture is the type of the property given in the record type. The following makes x a fixture and gives it the type int:

    { x: 10 } : { x: int }

The type of the value must be of the type of the field, or must be convertible to the type of the field.

If a property name in the record type matches a field name that is a getter and/or a setter then:

  • either the getter has no return type annotation (in which case the type from the record type will be applied to the getter) or the return type must be equal to the type present for the property in the record type;
  • either the setter has no parameter type annotation (in which case the type from the record type will be applied to the parameter) or the parameter type must be equal to the type present for the property in the record type; and
  • following resolution of the previous two points, the return type of the (generated) getter, the parameter type of the (generated) setter, and the type in the record type must all be equal.

Fields may be present in the field list that are not present in the type, but not vice versa. I.e., the following is legal:

    x = { x: 10, y: 20, z: 30 } : { x: int, y: int }

A field that does not have a matching explicit type annotation in the record type is dynamic, which is to say it is deletable. Note in particular that this applies to getters and setters. A getter/setter pair can only be deleted as a unit.

If a field name that has a getter/setter pair is not mentioned in the record type for the object initializer then the getter's return type must be the same as the setter's parameter type.

Namespaces

Fields are in the compatibility namespace noNS if they don't have an explicit qualifier.

NOTE   The use default namespace pragma does not apply to object initializers.

Enumerability

As outlined elsewhere [forthcoming enumerability spec], fixture properties are never enumerable. Dynamic fields are enumerable if they are in the namespace noNS and their enumerable bit is set.

All dynamic fields created by an object initializer have their enumerable bit set.

const

The const attribute introduces a fixture. The meaning of

    { const x: E }
is the same as the meaning of
    { x: E } : { x:* }
with the additional constraint that the writable bit on x is disabled (x is ReadOnly in ES3 lingo).

var

The var attribute introduces a fixture. The meaning of

    { var x: E }
is exactly the same as
    { x: E } : { x:* }

NOTE   That is, the "var" attribute is pure sugar and could be dropped without many consequences.

Getters and setters

A getter must not take any arguments.

A getter must not be declared to return void.

A setter may be declared as returning void but must not be declared as returning any other type.

If the program reads a property from an object and that property was named by a getter, then the getter method is invoked and the value returned by the getter method is returned to the program.

If the program writes a property to an object and that property was named by a setter, then the setter method is invoked with the value being written as its only argument. The value returned by the setter method, if any, is discarded.

Inside the getter and setter methods the value of this refers to the object on which the getter or setter was defined.

Rationale

(Not part of the final spec.)

An optional trailing comma comes from an early bug fix proposal. Everyone wants this.

Getters and setters have found a lot of use on the web and are a much-desired feature, even the ES3.1 group has been debating it. They are implemented in the form presented here in Firefox and Opera, at least.

const fields are motivated by the practical need to protect some object fields from being changed while staying within the easy to manage world of object initializers (ES4 classes would do the job but are more heavyweight by far). Structural type annotations cannot express what const can express, either. Some examples of the use of const are presented in the paper, "Evolutionary programming and gradual typing in ECMAScript 4", available from ecmascript.org.

var fields are similar to const fields but they can be expressed by structural type annotations and are really pure sugar. They were introduced late, in e-mail among some WG members but they seem "nice"; they allow the lightweight introduction of fixtures without the use of structural types.

Structural type annotations on object literals are a convenient shorthand for creating typed fixtures on objects without having to go the roundabout way through full classes. "I want to guarantee that these fields are here and that they have these types." It's lightweight integrity.

The new syntax is part of the evolutionary programming agenda and is yet another point on the continuum between ES3 programs and class-based ES4 programs (the syntax abstracts away from the type T that is the subject of new -- whether it's a class or a structural type).

_______________________________________________
Es4-discuss mailing list
Es4-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es4-discuss

Reply via email to