This document is now obsolete. See the new spec at https://tc39.github.io/proposal-optional-chaining/
This is an attempt to rewrite Optional Chaining without using a transient Nil reference for implementing short-circuiting semantics.
With this version:
- “optional construction” as in
new foo?.()
is abandoned; - constructions like
new a?.b
ora?.b `{c}`
are not allowed by the grammar; - parentheses as in
(a?.b).c
stop short-circuiting.
Note. A previous version of this document rewrote MemberExpression and CallExpression productions in order to be consistent with how OptionalChainingExpression is specified below. We removed that part, because:
- the consistency is incomplete, as there are still separate MemberExpression, CoverCallExpressionAndAsyncArrowHead and CallExpression productions for various technical reasons, and that leads to break a chain of property accesses, method calls, etc. in several parts;
- until otherwise proven, we don’t need to change what works.
What is important, is to ensure consistency of the observed behaviour (for that, factoring the relevant part of algorithms may help).
In the lexical grammar
OptionalChainingOperator ::
? . [lookahead ∉ DecimalDigit]
Then, adding to the syntax of Left-Hand-Side Expressions:
OptionalAccessChain :
OptionalChainingOperator [ Expression ]
OptionalChainingOperator IdentifierName
OptionalChainingOperator Arguments
OptionalAccessChain [ Expression ]
OptionalAccessChain . IdentifierName
OptionalAccessChain Arguments
Example of OptionalAccessChain: ?.a[b].c(d)
OptionalChainingExpression :
MemberExpression OptionalAccessChain
CallExpression OptionalAccessChain
OptionalChainingExpression OptionalAccessChain
Finally, the current LeftHandSideExpression
is replaced with:
NonOptionalChainingExpression :
NewExpression
CallExpression
LeftHandSideExpression :
NonOptionalChainingExpression
OptionalChainingExpression
The distinction between NonOptionalChainingExpression
and OptionalChainingExpression
is useful if/when we want to spec
optional assignment. (For optional deletion, nothing is needed, not even touching a jot in the relevant part of the spec.)
- “someNode.SomeOperation(foo, bar)” means “SomeOperation of someNode with arguments foo and bar”;
- “someNode.Evaluation()” means “the result of evaluating someNode”.
OptionalChainingExpression :
MemberExpression OptionalAccessChain
CallExpression OptionalAccessChain
OptionalChainingExpression OptionalAccessChain
- Let baseExpression be the first child of this production (i.e., this MemberExpression, CallExpression, or OptionalChainingExpression).
- Let ref be ? baseExpression.Evaluation().
- Let val be ? GetValue(ref).
- If Type(val) is Null or Undefined, then
- Return undefined.
- Return ? OptionalAccessChain.ChainEvaluation(ref, val).
This operation is defined for OptionalAccessChain productions.
OptionalAccessChain :
OptionalChainingOperator [ Expression ]
See https://tc39.github.io/ecma262/#sec-property-accessors-runtime-semantics-evaluation, omitting the two first steps, and replacing baseReference and baseValue with ref and val respectively.
OptionalAccessChain :
OptionalAccessChain OptionalChainingOperator [ Expression ]
- Let firstNode be the first child of this production.
- Let ref2 be ? firstNode.ChainEvaluation(ref, val).
- Let val2 be ? GetValue(ref2).
- Continue as in https://tc39.github.io/ecma262/#sec-property-accessors-runtime-semantics-evaluation, omitting the two first steps, and replacing baseReference and baseValue with ref2 and val2 respectively.
OptionalAccessChain :
OptionalAccessChain Arguments
- Let thisCallAccess be the parse of the production.
- Let tailCall be IsInTailPosition(thisCallAccess).
- Return ? EvaluateCall2(ref, val, Arguments, tailCall).
Here, EvaluateCall2(ref, func, Arguments, tailCall) is EvaluateCall(ref, Arguments, tailCall) with first step removed.
Other algorithms are left as exercise to the reader.