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.
--larsTitle: 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