11.1.5 Object Initialiser

2010-05-15

The object literal notation {} creates a new object, just as and exactly like new Object(); would. It will have no properties of its own and inherit only from Object.prototype. Unlike previous versions ES5 allows a comma at the end without a PropertyNameAndValueList following.


Syntax:

ObjectLiteral :
{ }
{ PropertyNameAndValueList }
{ PropertyNameAndValueList , }

PropertyNameAndValueList :
PropertyAssignment
PropertyNameAndValueList , PropertyAssignment

PropertyAssignment :
PropertyName : AssignmentExpression
get PropertyName ( ) { FunctionBody }
set PropertyName ( PropertySetParameterList { FunctionBody }

PropertyName :
IdentifierName
StringLiteral
NumericLiteral

PropertySetParameterList :
Identifier

A few interesting things right off the bat: You can create getters and setters like this:

Code: (Ecma)
var y = 5;
var x = { get val(){ return y; }, put val(n){ y = n; } };
y; // 5
x.val = 15;
y; // 15
y = 19;
x.val; // 19

Another interesting fact is that the parameter list for the setter can only hold one name, if you for some reason supply more it should not parse properly (but it might anyways, if some lazy parsing is used). Properties of an object in the literal notation may be any Identifier, string or number. A literal with just a comma is not allowed.

Semantics:

ObjectLiteral : { }

Code: (Meta Ecma)
function evaluate({ }) {
return new Object();
}


ObjectLiteral : { PropertyNameAndValueList }
ObjectLiteral : { PropertyNameAndValueList , }

Code: (Meta Ecma)
function evaluate({ PropertyNameAndValueList ,(opt) }) {
return evaluate(PropertyNameAndValueList);
}


PropertyNameAndValueList : PropertyAssignment

Code: (Meta Ecma)
function evaluate(PropertyAssignment) {
var obj = new Object;
var propId = evaluate(PropertyAssignment);
obj.[[DefineOwnProperty]](propId.name, propId.descriptor, false);
return obj;
}


PropertyNameAndValueList : PropertyNameAndValueList , PropertyAssignment

Code: (Meta Ecma)
function evaluate(PropertyNameAndValueList , PropertyAssignment) {
var obj = evaluate(PropertyNameAndValueList);
var propId = evaluate(PropertyAssignment);
var previous = obj.[[GetOwnProperty]]("propId");
if (previous !== undefined) {
if (global.arrExecutionContexts[global.arrExecutionContexts.length-1].strict && IsDataDescriptor(previous) && IsDataDescriptor(propId.descriptor)) throw SyntaxError;
if (IsDataDescriptor(previous) && IsAccessorDescriptor(propId.descriptor)) throw SyntaxError;
if (IsAccessorDescriptor(previous) && IsDataDescriptor(propId.descriptor)) throw SyntaxError;
if (IsAccessorDescriptor(previous) && IsAccessorDescriptor(propId.descriptor) && (([[Get]] in previous && [[Get]] in propId.descriptor) || ([[Put]] in previous && [[Put]] in propId.descriptor))) throw SyntaxError;
}
obj.[[DefineOwnProperty]](propId.name, propId.descriptor, false);
return obj;
}


The conditions under which a syntax error is thrown are probably all meant for strict mode, but under the current version that only applies to the first condition. Those conditions are trying to make sure you don't reclare a property that's already been declared as such in the same literal invocation. If at any point the syntax error is thrown, an implementation must treat this as an early error (clause 16).

PropertyAssignment : PropertyName : AssignmentExpression

Code: (Meta Ecma)
function evaluate(PropertyName : AssignmentExpression) {
var propName = evaluate(PropertyName);
var exprValue = evaluate(AssignmentExpression);
var propValue = GetValue(exprValue);
var desc = PD{[[Value]]:propValue, [[Writable]]:true, [[Enumerable]]:true, [[Configurable]]:true};
return new PropertyIdentifier(propName, desc); // .name and .descriptor, see above
}


PropertyAssignment : get PropertyName ( ) { FunctionBody }

Code: (Meta Ecma)
function evaluate(get PropertyName ( ) { FunctionBody }) {
var propName = evaluate(PropertyName);
var closure = new Function(null, FunctionBody, global.arrExecutionContexts[global.arrExecutionContexts.length-1].LexicalEnvironment, global.arrExecutionContexts[global.arrExecutionContexts.length-1].strict || FunctionBody.strict); // 13.2, create new (internal) function
var desc = PD{[[Get]]:closure, [[Enumerable]]:true, [[Configurable]]:true};
return PropertyIdentifier(propName, desc); // .name .descriptor
}


PropertyAssignment : set PropertyName ( PropertySetParameterList ) { FunctionBody }

Code: (Meta Ecma)
function evaluate(get PropertyName ( PropertySetParameterList ) { FunctionBody }) {
var propName = evaluate(PropertyName);
var closure = new Function(PropertySetParameterList, FunctionBody, global.arrExecutionContexts[global.arrExecutionContexts.length-1].LexicalEnvironment, global.arrExecutionContexts[global.arrExecutionContexts.length-1].strict || FunctionBody.strict); // 13.2, create new (internal) function
var desc = PD{[[Set]]:closure, [[Enumerable]]:true, [[Configurable]]:true};
return PropertyIdentifier(propName, desc); // .name .descriptor
}


In strict mode (if the running execution context is strict or if the FunctionBody is strict), if "eval" or "arguments" occur as the Identifier in a PropertySetParameterList of a PropertyAssignment a SyntaxError is thrown. This prevents further mucking about.

PropertyName : IdentifierName

Code: (Meta Ecma)
function evaluate(IdentifierName) {
return IdentifierName; // "string value of IdentifierName"...
}


PropertyName : StringLiteral

Code: (Meta Ecma)
function evaluate(StringLiteral) {
return StringLiteral; // or rather, the value of it..
}


PropertyName : NumericLiteral

Code: (Meta Ecma)
function evaluate(NumericLiteral) {
var nbr = NumericLiteral; // value of
return ToString(nbr);
}