Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Revert previous spec alterations #75

Merged
merged 2 commits into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value
```

The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods:

```js
iterator.return?.() // manually close an iterator
```
or with methods not universally implemented:
```js
if (myForm.checkValidity?.() === false) { // skip the test in older web browsers
// form validation fails
return;
}
```

## Prior Art
The following languages implement the operator with the same general semantics as this proposal (i.e., 1) guarding against a null base value, and 2) short-circuiting application to the whole chain):
* C#: [Null-conditional operator](https://msdn.microsoft.com/en-us/library/dn986595.aspx) — null-conditional member access or index, in read access.
Expand All @@ -46,6 +59,7 @@ The Optional Chaining operator is spelled `?.`. It may appear in three positions
```javascript
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
```

### Notes
Expand All @@ -66,7 +80,11 @@ a == null ? undefined : a[x]

a?.b()       // undefined if `a` is null/undefined
a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function
// otherwise, evaluates to `a.b()`
                            // otherwise, evaluates to `a.b()`

a?.()       // undefined if `a` is null/undefined
a == null ? undefined : a() // throws a TypeError if `a` is neither null/undefined, nor a function
            // invokes the function `a` otherwise
```

### Short-circuiting
Expand Down Expand Up @@ -99,9 +117,9 @@ Let’s call *Optional Chain* an Optional Chaining operator followed by a chain
An Optional Chain may be followed by another Optional Chain.

```js
a?.b?.['foo'].c(x).d
a == null ? undefined : a.b['foo'] == null ? undefined : a.b['foo'].c(x).d
// (as always, except that `a` and `a.b['foo']` are evaluated only once)
a?.b[3].c?.(x).d
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
// (as always, except that `a` and `a.b[3].c` are evaluated only once)
```

### Edge case: grouping
Expand All @@ -117,12 +135,19 @@ That follows from the design choice of specifying the scope of short-circuiting

Note that, whatever the semantics are, there is no practical reason to use parentheses in that position anyway.

### Optional deletion

Because the `delete` operator is very liberal in what it accepts, we have that feature for free:
```js
delete a?.b
// delete (a == null ? undefined : a.b) // that *would* work if `? :` could return a Reference...
a == null ? undefined : delete a.b // this is what we get, really
```

## Not supported

Although they could be included for completeness, the following are not supported due to lack of real-world use cases or other compelling reasons; see [Issue # 22](https://github.com/tc39/proposal-optional-chaining/issues/22) and [Issue #54](https://github.com/tc39/proposal-optional-chaining/issues/54) for discussion:

* optional function execution: `a?.()`
* optional deletion: `delete a?.b`
* optional construction: `new a?.()`
* optional template literal: ``a?.`{b}` ``
* constructor or template literals in/after an Optional Chain: `new a?.b()`, ``a?.b`{c}` ``
Expand All @@ -141,16 +166,16 @@ All the above cases will be forbidden by the grammar or by static semantics so t
<dl>


<dt>obj?.[expr] looks ugly. Why not use obj?[expr] as does &lt;language X>?
<dt>obj?.[expr] and func?.(arg) look ugly. Why not use obj?[expr] and func?(arg) as does &lt;language X>?

<dd>

We don’t use the `obj?[expr]` syntax, because of the difficulty for the parser to efficiently distinguish those forms from the conditional operator, e.g., `obj?[expr].filter(fun):0`.
We don’t use the `obj?[expr]` and `func?(arg)` syntax, because of the difficulty for the parser to efficiently distinguish those forms from the conditional operator, e.g., `obj?[expr].filter(fun):0` and `func?(x - 2) + 3 :1`.

Alternative syntaxes for those two cases each have their own flaws; and deciding which one looks the least bad is mostly a question of personal taste. Here is how we made our choice:

* pick the best syntax for the `obj?.prop` case, which is expected to occur most often;
* extend the use of the recognisable `?.` sequence of characters to other cases: `obj?.[expr]`.
* extend the use of the recognisable `?.` sequence of characters to other cases: `obj?.[expr]`, `func?.(arg)`.

As for &lt;language X>, it has different syntactical constraints than JavaScript because of &lt;some construct not supported by X or working differently in X>.

Expand Down
74 changes: 69 additions & 5 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ <h2>Syntax</h2>
OptionalChain[Yield, Await] :
OptionalChainingPunctuator `[` Expression[+In, ?Yield, ?Await] `]`
OptionalChainingPunctuator IdentifierName
OptionalChainingPunctuator Arguments[?Yield, ?Await]
OptionalChainingPunctuator TemplateLiteral[?Yield, ?Await, +Tagged]
OptionalChain[?Yield, ?Await] `[` Expression[+In, ?Yield, ?Await] `]`
OptionalChain[?Yield, ?Await] `.` IdentifierName
Expand Down Expand Up @@ -477,9 +478,64 @@ <h1>The `new` Operator (<a href="https://tc39.github.io/ecma262/#sec-new-operato

<emu-clause id="sec-function-calls">
<h1>Function Calls (<a href="https://tc39.github.io/ecma262/#sec-function-calls">12.3.4</a>)</h1>

<p><i>(not modified)</i></p>
</emu-clause>

<emu-clause id="sec-function-calls-runtime-semantics-evaluation">
<h1>Runtime Semantics: Evaluation</h1>
<emu-grammar>CallExpression : CoverCallExpressionAndAsyncArrowHead</emu-grammar>
<emu-alg>
1. Let _expr_ be CoveredCallExpression of |CoverCallExpressionAndAsyncArrowHead|.
1. Let _memberExpr_ be the |MemberExpression| of _expr_.
1. Let _arguments_ be the |Arguments| of _expr_.
1. Let _ref_ be the result of evaluating _memberExpr_.
1. Let _func_ be ? GetValue(_ref_).
1. If Type(_ref_) is Reference, IsPropertyReference(_ref_) is *false*, and GetReferencedName(_ref_) is `"eval"`, then
1. If SameValue(_func_, %eval%) is *true*, then
1. Let _argList_ be ? ArgumentListEvaluation of _arguments_.
1. If _argList_ has no elements, return *undefined*.
1. Let _evalText_ be the first element of _argList_.
1. If the source code matching this |CallExpression| is strict mode code, let _strictCaller_ be *true*. Otherwise let _strictCaller_ be *false*.
1. Let _evalRealm_ be the current Realm Record.
1. Perform ? HostEnsureCanCompileStrings(_evalRealm_, _evalRealm_).
1. Return ? PerformEval(_evalText_, _evalRealm_, _strictCaller_, *true*).
1. Let _thisCall_ be this |CallExpression|.
1. Let _tailCall_ be IsInTailPosition(_thisCall_).
1. Return ? EvaluateCall(_func_, _ref_, _arguments_, _tailCall_).
</emu-alg>
<p>A |CallExpression| evaluation that executes step 6.a.vii is a <dfn>direct eval</dfn>.</p>
<emu-grammar>CallExpression : CallExpression Arguments</emu-grammar>
<emu-alg>
1. Let _ref_ be the result of evaluating |CallExpression|.
1. Let _func_ be ? GetValue(_ref_).
1. Let _thisCall_ be this |CallExpression|.
1. Let _tailCall_ be IsInTailPosition(_thisCall_).
1. Return ? EvaluateCall(_func_, _ref_, |Arguments|, _tailCall_).
</emu-alg>
</emu-clause>

<emu-clause id="sec-evaluatecall" aoid="EvaluateCall" oldids="sec-evaluatedirectcall">
<h1>Runtime Semantics: EvaluateCall ( _func_, _ref_, _arguments_, _tailPosition_ )</h1>
<p>The abstract operation EvaluateCall takes as arguments a value _func_, a value _ref_, a Parse Node _arguments_, and a Boolean argument _tailPosition_. It performs the following steps:</p>
<emu-alg>
1. If Type(_ref_) is Reference, then
1. If IsPropertyReference(_ref_) is *true*, then
1. Let _thisValue_ be GetThisValue(_ref_).
1. Else the base of _ref_ is an Environment Record,
1. Let _refEnv_ be GetBase(_ref_).
1. Let _thisValue_ be _refEnv_.WithBaseObject().
1. Else Type(_ref_) is not Reference,
1. Let _thisValue_ be *undefined*.
1. Let _argList_ be ArgumentListEvaluation of _arguments_.
1. ReturnIfAbrupt(_argList_).
1. If Type(_func_) is not Object, throw a *TypeError* exception.
1. If IsCallable(_func_) is *false*, throw a *TypeError* exception.
1. If _tailPosition_ is *true*, perform PrepareForTailCall().
1. Let _result_ be Call(_func_, _thisValue_, _argList_).
1. Assert: If _tailPosition_ is *true*, the above call will not return here, but instead evaluation will continue as if the following return has already occurred.
1. Assert: If _result_ is not an abrupt completion, then Type(_result_) is an ECMAScript language type.
1. Return _result_.
</emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-super-keyword">
<h1>The `super` Keyword (<a href="https://tc39.github.io/ecma262/#sec-super-keyword">12.3.5</a>)</h1>
Expand All @@ -501,7 +557,8 @@ <h1>Tagged Templates (<a href="https://tc39.github.io/ecma262/#sec-tagged-templa
<ins class="block">
<emu-clause id="sec-optional-chains">
<h1>Optional Chains</h1>
<emu-note>An optional chain is a chain of property accesses introduced by an |OptionalChainingPunctuator|.</emu-note>
<emu-note>An optional chain is a chain of property accesses and function calls introduced by an |OptionalChainingPunctuator|.</emu-note>
</emu-note>
<emu-clause id="sec-optional-chaining-evaluation">
<h1>Runtime Semantics: Evaluation</h1>
<emu-grammar>
Expand Down Expand Up @@ -533,6 +590,12 @@ <h1>Runtime Semantics: ChainEvaluation</h1>
1. If the code matched by this production is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
1. Return ? EvaluateStaticPropertyAccess(_baseValue_, |IdentifierName|, _strict_).
</emu-alg>
<emu-grammar>OptionalChain : OptionalChainingPunctuator Arguments</emu-grammar>
<emu-alg>
1. Let _thisChain_ be this production.
1. Let _tailCall_ be IsInTailPosition(_thisChain_).
1. Return ? EvaluateCall(_baseValue_, _baseReference_, |Arguments|, _tailCall_).
</emu-alg>
<emu-grammar>OptionalChain : OptionalChain `[` Expression `]`</emu-grammar>
<emu-alg>
1. Let _optionalChain_ be this |OptionalChain|.
Expand Down Expand Up @@ -606,6 +669,7 @@ <h1>Expression Rules</h1>
</emu-alg>
<emu-grammar>
OptionalChain :
OptionalChainingPunctuator Arguments
OptionalChain Arguments
</emu-grammar>
<emu-alg>
Expand All @@ -618,4 +682,4 @@ <h1>Expression Rules</h1>
</emu-clause>
<!--
</emu-clause>
-->
-->