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

Commit

Permalink
Merge pull request #75 from tc39/revert
Browse files Browse the repository at this point in the history
Revert previous spec alterations
  • Loading branch information
dusave authored Nov 30, 2018
2 parents 6805300 + 22ba2ca commit e8c8c60
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 14 deletions.
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>
-->
-->

0 comments on commit e8c8c60

Please sign in to comment.