Translator: Li Songfeng
The purpose of this article is to try to provide the translation of the core terms in the ECMAScript specification for the evaluation of the peers.
In this article, we’ll look for a simple feature from the specification to help us understand the notation in the specification. Let’s go!
preface
Even if you know JavaScript, reading its specifications can be daunting.
Let’s start with a concrete example and understand it through the specification. The following code illustrates the Object. The prototype. The hasOwnProperty usage:
const o = { foo: 1 };
o.hasOwnProperty('foo'); // true
o.hasOwnProperty('bar'); // false
Copy the code
O does not have a property called hasOwnProperty, so look up the prototype chain. So, I found it in the Object. Prototype of O.
To describe the Object. The prototype. The working principle of the property specification used a similar pseudo code:
Object. The prototype. The hasOwnProperty V (V) in the parameter called hasOwnProperty method, will perform the following steps: 1. The P is? ToPropertyKey (V); 2. Let O be? ToObject (this); 3. Return? HasOwnProperty (O, P).
As well as
HasOwnProperty(O, P) The abstract operation HasOwnProperty is used to determine whether an object has its own property with the specified property as the key. Returns a Boolean value. This operation is called with arguments O and P, where O is the object and P is the property key. This abstract operation performs the following steps. 1. Assert :Type(O) is Object; 2. Assert :IsPropertyKey(P) is true; 3. Do you have any questions? O. [[GetOwnProperty]] (p); 4. If desc is undefined, return false; 5. Return true.
What is an “abstract operation”? What does the [[]] inside represent? Why put one? In front of the function? And what does “assert” mean?
Language types versus specification types
The specification uses undefined, true, and false, which we already know in JavaScript. These are language values, the values of the language types defined in the specification.
The specification also uses language values internally, such as a field of an internal data type that may contain true or false. JavaScript engines, by contrast, typically do not use language values internally. For example, if the JavaScript engine is written in C++, it will usually use C++ true and false, which are not internal representations of the JavaScript language values true and false.
In addition to language types, specifications have their own specification types. Canonical types are types that exist only in the specification, not in the JavaScript language. JavaScript engines don’t need (but can) implement them. This article introduces the specification type Record and its subtype Completion Record.
Abstract operations
Abstract operations are functions defined by the ECMAScript specification to make the specification more concise. The JavaScript engine does not have to implement these functions internally. These functions cannot be called directly in JavaScript.
Internal fields and internal methods
Internal slots and internal methods are contained in [[]].
An internal field is a data member of a JavaScript object or specification type that stores the state of the object. Internal methods are internal member functions of JavaScript objects.
For example, each JavaScript object has an internal field [[Prototype]] and an internal method [[GetOwnProperty]].
Inner fields and inner methods cannot be used in JavaScript. In other words, you cannot access o.[[Prototype]] or call O.[[GetOwnProperty]](). JavaScript engines can implement them for internal use, but are not required.
Sometimes internal methods delegate to abstract operations with similar names, such as [[GetOwnProperty]] for ordinary Objects:
[[[GetOwnProperty]](p)] when the internal method [[GetOwnProperty]] is called with the property key P, the following steps are performed: 1. Return! OrdinaryGetOwnProperty (O, P).
(The next article will explain what this exclamation point means.)
The OrdinaryGetOwnProperty is not an internal method because it is not associated with any object, but takes the object to operate on as a parameter.
The word “ordinary” in front of OrdinaryGetOwnProperty means that it operates only on ordinary objects. ECMAScript objects are either ordinary or exotic. Ordinary objects must have default behavior defined by a set of methods called essential Internal methods. If an object modifies the default behavior (that is, overrides or overrides one or more basic internal methods). It is a heterogeneous object.
Arrays are most familiar as heterogeneous objects because their Length property behaves differently than the default: setting the length property of the Array may remove elements from the Array.
All the basic internal methods are shown here (11 ordinary objects and 2 function objects). (Translator’s Note).
Complete records
What do the question marks and exclamation marks in the previous example mean? To understand them, you need to understand the Completion Record!
Completion records are a specification type (used only in the specification). The JavaScript engine does not need to implement corresponding internal data types.
A completion Record is a Record type that has a fixed set of named fields. The completion record has the following three fields.
All abstract operations implicitly return a completion record. Even if an abstract operation appears to return a value of a simple type (such as Boolean), that value is implicitly returned wrapped in a completion record of type Normal (normally completed) (see implicit completion value).
Note 1: The specification itself is not entirely consistent in this regard. There are some helper functions that return bare values that are used directly and do not need to be extracted from the completion record. But this is usually clear in context. Note 2: Specification editors are also working on more explicit handling of completion records.
If an algorithm throws an exception, it means that the completion record returned has [[Type]] as a throw and [[Value]] as an exception object. We will not discuss the break, continue, and return types here (there are no examples in the specification because they cannot cross functions. (Translator’s Note).
ReturnIfAbrupt(argument) indicates that the following steps are performed:
1. If argument is hard, return to argument. 2. Set argument to argument.[[Value]].
In other words, for a completed record, if it is hard done, it is returned immediately; If the completion is normal, the value of the completion record is extracted.
ReturnIfAbrupt looks like a function call, but it’s not. The ReturnIfAbrup causes the function at its location to return, not the ReturnIfAbrup itself. ReturnIfAbrupt is a bit like a macro in C.
ReturnIfAbrupt can be used like this:
1. Set obj to Foo(); (OBJ is a completion record.) 2. ReturnIfAbrupt (obj); 3. The Bar (obj). (At this point, obj has become the value extracted from the completion record.)
Now comes the question mark:? Foo() is equivalent to ReturnIfAbrupt(Foo()). Obviously, using abbreviations (?) You can save yourself the trouble of writing error-handling code explicitly every time.
Similarly, “Make val! Foo() “is equivalent to:
1. Set val to Foo(); (Val is a completion record.) 2. Assert: val is not mandatory; 3. Set val to val.[[Value]].
(In other words, the exclamation mark indicates the extraction of the value from the normal completion record. (Translator’s Note)
After know these, you can put in front of the Object. The prototype. The hasOwnProperty rewritten in the form of complete but redundancy is as follows:
Object.prototype.hasOwnProperty(V) 1. Let P be ToPropertyKey(V); 2. If P is hard completed, return P. 3. Set P as P.[[Value]]; 4. Set O to ToObject(this value); 5. If O is hard completed, O is returned. 6. Set O to O.[[Value]]; 7. Set temp to HasOwnProperty(O, P); 8. If temp is hard, return temp. Set temp to temp.[[Value]]; 10. Return NormalCompletion(temp).
Rewrite HasOwnProperty() as follows:
HasOwnProperty(O, P)\
1. Assert: Type(O) is Object; 2. Assert: IsPropertyKey(P) is true; O.[[GetOwnProperty]](p); 4. If desc is hard completed, return desc. Set desc to desc.[[Value]]; 6. If desc is undefined, return NormalCompletion(false); Return NormalCompletion(true).
The inner method O.[[GetOwnProperty]] is rewritten without an exclamation point as follows:
O.[[GetOwnProperty]] 1. Set temp to OrdinaryGetOwnProperty(O, P); 2. Assert :temp is not mandatory complete; Set temp to temp.[[Value]]; 4. Return NormalCompletion(temp);
It is assumed that temp is a new temporary variable that does not conflict with any other variables.
This also applies to the previous statement that when a return statement returns a non-complete record, the return value is actually wrapped implicitly in a NormalCompletion.
Extended learning: Return? Foo()
The specification uses “return? Foo(), “why is there a question mark?
“Return? Foo() “extends to:
Set temp to Foo(); 2. If temp is hard, return temp. Set temp to temp.[[Value]]. 4. Return NormalCompletion(temp).
This is no different from “returning Foo()” : the behavior is the same whether it is hard or normal.
Write it as “Return? Foo() “is only for editorial convenience, to make it clear that the returned Foo() is a complete record.
assertions
The “assertions” in the specification indicate unchanging conditions in the algorithm. These “assertions” are added for clarity and no implementation is required. In other words, the implementation does not need to check these conditions.
challenge
Abstract operations also delegate to other abstract operations (see figure below), but based on the introduction of this article, you should be able to infer what these operations end up doing. And then you get a Property Descriptor, which is a canonical type.
summary
We saw a simple method by specification Object. The prototype. The hasOwnProperty and it calls the abstract operation, know? And! Related to error handling, you also learned about language types, specification types, internal fields, and internal methods.
The original link
Welcome to “Byte front-end ByteFE” resume delivery email “[email protected]”