BindingIdentifier与IdentifierReference的区别是什么?
Great question! At first glance, these two productions look identical, but they serve completely different roles in the ECMAScript grammar—this is a classic case of syntactic context defining meaning, even when the tokens are the same.
First, let's recap the relevant grammar productions from the spec:
IdentifierReference[Yield, Await]: Identifier [~Yield]yield [~Await]await BindingIdentifier[Yield, Await]: Identifier [~Yield]yield [~Await]await Identifier: IdentifierName but not ReservedWord
Core Differences Between the Two
1. Purpose: Reference vs. Binding
- IdentifierReference is for looking up an existing name (reading its value). It's used whenever you want to access a variable, function, or property that's already been declared.
- Example:
const total = price + tax— herepriceandtaxare IdentifierReferences, since we're referencing values that should already exist in scope.
- Example:
- BindingIdentifier is for creating a new name binding (declaring a variable/identifier). It's how you introduce a new name into the current scope.
- Example:
let tax = 0.08— heretaxis a BindingIdentifier, since we're defining a new variable.
- Example:
2. Grammatical Context
The spec uses these productions in entirely separate contexts:
- IdentifierReference appears in expression contexts: think function calls (
myFunc()), member access (obj.prop), or any place where you're evaluating a value. - BindingIdentifier appears in declaration contexts: variable declarations (
var/let/const), function parameters, function names, catch clause bindings, destructuring patterns (const { name } = user), etc.
3. Reserved Word Handling
While both allow yield and await under specific conditions, the rules differ based on their role:
- For IdentifierReference:
yieldis allowed as an identifier only in non-generator functions, andawaitonly in non-async functions. In generator/async code, they act as keywords and can't be referenced as identifiers. - For BindingIdentifier: You can declare
let yield;orconst await;only in contexts where the Yield/Await grammar parameter is set to~(i.e., non-generator/non-async code). Inside a generator function, declaringlet yield;is invalid, since the context enforcesyieldas a keyword.
Why This Distinction Matters
This separation eliminates ambiguity in the grammar. Without it, the parser couldn't tell if a token like x is meant to create a new binding (e.g., in let x = 5) or reference an existing one (e.g., console.log(x)). It also lets the spec enforce strict scoping rules, ensuring that names are declared before they're referenced, and that reserved words are handled correctly depending on the context.
内容的提问来源于stack exchange,提问作者MaximPro

