diff --git a/README.md b/README.md index c953d5e..ed1f2dd 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Current Stage: ## Authors * Claude Pache (@claudepache) * Gabriel Isenberg (@the_gisenberg) +* Dustin Savery (@dustinsavery) ## Overview and motivation When looking for a property value that's deep in a tree-like structure, one often has to check whether intermediate nodes exist: @@ -29,19 +30,6 @@ 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. @@ -58,7 +46,6 @@ 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 @@ -79,11 +66,7 @@ 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()` - -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 + // otherwise, evaluates to `a.b()` ``` ### Short-circuiting @@ -116,9 +99,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[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) +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) ``` ### Edge case: grouping @@ -134,19 +117,12 @@ 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}` `` @@ -165,16 +141,16 @@ All the above cases will be forbidden by the grammar or by static semantics so t
-
obj?.[expr] and func?.(arg) look ugly. Why not use obj?[expr] and func?(arg) as does <language X>? +
obj?.[expr] looks ugly. Why not use obj?[expr] as does <language X>?
-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`. +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`. 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]`, `func?.(arg)`. +* extend the use of the recognisable `?.` sequence of characters to other cases: `obj?.[expr]`. As for <language X>, it has different syntactical constraints than JavaScript because of <some construct not supported by X or working differently in X>. diff --git a/spec.html b/spec.html index 32356e6..2b9de59 100644 --- a/spec.html +++ b/spec.html @@ -157,7 +157,6 @@

Syntax

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 @@ -478,64 +477,9 @@

The `new` Operator (

Function Calls (12.3.4)

- - -

Runtime Semantics: Evaluation

- CallExpression : CoverCallExpressionAndAsyncArrowHead - - 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_). - -

A |CallExpression| evaluation that executes step 6.a.vii is a direct eval.

- CallExpression : CallExpression Arguments - - 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_). - -
- - -

Runtime Semantics: EvaluateCall ( _func_, _ref_, _arguments_, _tailPosition_ )

-

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:

- - 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_. - -
- + +

(not modified)

+

The `super` Keyword (12.3.5)

@@ -557,8 +501,7 @@

Tagged Templates (

Optional Chains

- An optional chain is a chain of property accesses and function calls introduced by an |OptionalChainingPunctuator|. - + An optional chain is a chain of property accesses introduced by an |OptionalChainingPunctuator|.

Runtime Semantics: Evaluation

@@ -590,12 +533,6 @@

Runtime Semantics: ChainEvaluation

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_). - OptionalChain : OptionalChainingPunctuator Arguments - - 1. Let _thisChain_ be this production. - 1. Let _tailCall_ be IsInTailPosition(_thisChain_). - 1. Return ? EvaluateCall(_baseValue_, _baseReference_, |Arguments|, _tailCall_). - OptionalChain : OptionalChain `[` Expression `]` 1. Let _optionalChain_ be this |OptionalChain|. @@ -669,7 +606,6 @@

Expression Rules

OptionalChain : - OptionalChainingPunctuator Arguments OptionalChain Arguments @@ -682,4 +618,4 @@

Expression Rules

+--> \ No newline at end of file