This article brings you a detailed analysis (example) of the ToPrimitive abstract operation in the ECMAScript7 specification. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
This article will introduce the ToPrimitive abstract operation in the ECMAScript7 specification.
ECMAScript data types are subdivided into two major categories of data types, one is the language type and the other is the specification type:
Language types are data types that can be used directly by developers;
Canonical types represent meta-values (meta-values), which are used in algorithms to describe the semantics of ECMAScript language structures and language types. They are mainly used for specification description and do not need to be actually implemented.
There are 7 language types in ECMAScript:
Undefined
Null
Boolean, Boolean type
String, string type
Symbol, symbol type
Number, number type
Object, object type
The original data types are the above Undefined, Null, Boolean, String, Symbol and Number, which are non-object data types.
The standard type mentioned below is only List, which is a list, similar to an array, represented by the symbol « ».
Symbol has many famous symbols, such as @@toPrimitive, which is Symbol.toPrimitive, which is an attribute defined on the Symbol object.
This abstract operation accepts a parameter input and an optional parameter PreferredType. The purpose of this abstract operation is to convert the parameter input into a non-object data type, that is, a primitive data type. If the input can be converted into multiple raw data at the same time, the value of PreferredType will be referenced first. Refer to the following table for the conversion process:
Data type of parameter input
|
Result |
---|---|
Undefined | Return input itself |
Null | Return input itself |
Boolean | Return input itself |
Number | Return input itself |
String | Return input itself |
Symbol | Return input itself |
Object | Perform the following steps |
If the input data type is an object, perform the following steps:
1. If the PreferredType parameter is not passed in, let hint equal "default";
2. If PreferredType is hint String, let hint be equal to "string";
3. If PreferredType is hint Number, let hint be equal to "number";
4. Let exoticToPrim be equal to GetMethod(input, @@toPrimitive), The approximate semantics is the @@toPrimitive method to obtain the parameter input;
5. If exoticToPrim is not Undefined, then:
Let result equal to Call(exoticToPrim, input, « hint »), the approximate semantics is Execute exoticToPrim(hint);
If result is a primitive data type, return result;
Throw an exception of type error;
6. If hint is "default", Let hint equal "number";
7. Return the result of the OrdinaryToPrimitive(input, hint) abstract operation.
The data type of O is an object, the data type of hint is a string, and the value of hint is either "string" or "number". The steps of this abstract operation are as follows:
1. If hint is "string", let methodNames equal « "toString", "valueOf" »;
2. If hint is "number", Let methodNames equal « "valueOf", "toString" »;
3. Iterate the list of methodNames in order, for each iteration value name:
Let method equal Call(method, O), The approximate semantics is to execute method();
If the type of result is not an object, return result;
Let method equal Get(O, name), the approximate semantics is to obtain the name value corresponding to object O Attributes;
If the method can be called, then:
4. Throw an exception of the wrong type.
It can be seen from the above steps:
It can be seen from step 6 of ToPrimitive that when the optional parameter PreferredType is not provided, hint will default to "number";
Passed Step 4 of ToPrimitive shows that you can override the default behavior by defining the @@toPrimitive method. For example, the Date object and Symbol object defined in the specification both have the @@toPrimitive method defined on the prototype.
Some people may ask why we need to explain the abstract methods in the specification. I don’t use abstract methods. In fact, this method is used in many places, but you just don’t know it. Below, we will explain a few examples to help you deepen your understanding of it.
'' + [1, 2, 3] // "1,2,3"
According to the addition operation in the specification, for the operation x y, ToPrimitive(x) and ToPrimitive(y) will be called to convert x and y into Primitive data type. In the above example, '' itself is a primitive data type, so '' itself is returned. [1, 2, 3] is an object type, and the array does not define the @@toPrimitive property. Because the PreferredType is not provided, in step 6 of the ToPrimitive operation, the hint becomes "number", so the methodNames in OrdinaryToPrimitive are « "valueOf", "toString" ».
var a = [1, 2, 3] a.valueOf() // [1, 2, 3],数组a本身 a.toString() // "1,2,3"
Because valueOf returns the array a itself or the object type, it will continue to call the toString method and return the string "1,2,3", so
'' + [1, 2, 3] // => '' + '1,2,3' => '1,2,3'
Then, if We override the valueOf method on the array prototype so that the method returns a primitive data type. So what will the result be?
var a = [1, 2, 3] a.valueOf = function () { console.log('trigger valueOf') return 'hello' } '' + a // => '' + 'hello' => 'hello'
After overriding the default valueOf, calling valueOf will return the original data type. According to 3.2.2 of OrdinaryToPrimitive, it returns directly at this time and the toString method will not be called again. At the same time, "trigger valueOf" will be logged on the console, which means that valueOf is indeed called.
So, if we override the default toString method of the array so that the method returns the object type, what will be the result?
var a = [1, 2, 3] a.toString = function () { console.log('trigger toString') return this } '' + a // Uncaught TypeError: Cannot convert object to primitive value
Because the valueOf method on the array prototype returns the object type, in the above example, we overwrite toString so that it also returns the object type, then we will go directly to step 4 of OrdinaryToPrimitive, that is It just throws a type error exception and cannot convert the object into the original data type.
We mentioned above that you can customize the behavior of ToPrimitive through the @@toPrimitive method, such as the following example:
var a = [1, 2, 3] a[Symbol.toPrimitive] = function () { return 'custom' } '' + a // => '' + 'custom' => 'custom'
The addition operation does not provide a PreferredType when calling ToPrimitive. Let’s talk about it next. Example of preferentially using hint String as PreferredType:
var a = [1, 2, 3] a.valueOf = function () { console.log('trigger valueOf') return 'hello' } a.valueOf() // "hello" a.toString() // "1,2,3" var obj = {} obj[a] = 'hello' // obj是{1,2,3: "hello"}
When using a variable as a key value, ToPrimitive will be called to convert the key value into the original data type, and the value of PreferredType is hint String. It can also be seen from the above example that the results of a.valueOf and a.toString are both strings, but '1,2,3' is used, which is the result of using a.toString. Of course, if we redefine the toString method and return the object, then the value of valueOf will be used:
var a = [1, 2, 3] a.valueOf = function () { console.log('trigger valueOf') return 'hello' } a.toString = function () { console.log('trigger toString') return this } var obj = {} obj[a] = 'hello' // obj是{hello: "hello"}
and "trigger toString" will be logged on the console first, and then "trigger valueOf" will be logged. Of course, if both of these return objects, an error will still be reported:
var a = [1, 2, 3] // 使用原型链上的valueOf方法 a.toString = function () { console.log('trigger toString') return this } var obj = {} obj[a] = 'hello' // Uncaught TypeError: Cannot convert object to primitive value
Date
在上面讲ToPrimitive的时候,提到Date对象和Symbol对象在原型上定义了@@toPrimitive方法。在ToPrimitive的第6步的操作中,我们可以看到当没有提供PreferredType的时候,优先调用valueOf方法。Date原型上的@@toPrimitive做的事情非常简单:当没有提供PreferredType的时候,优先调用toString方法。所以对于上面的操作,Date对象的行为是不一样的:
var a = [1, 2, 3] a.valueOf = function () { return 'hello' } a.valueOf() // "hello" a.toString() // "1,2,3" '' + a // "hello" var date = new Date() date.valueOf() // 1536416960724 date.toString() // "Sat Sep 08 2018 22:29:20 GMT+0800 (中国标准时间)" '' + date // "Sat Sep 08 2018 22:29:20 GMT+0800 (中国标准时间)"
我们可以看到date的valueOf方法和toString方法都返回原始数据类型,但是优先使用了toString方法。
总结
本文主要讲解了ToPrimitive抽象操作,以及一些相关的例子,希望大家能有所收获。
The above is the detailed content of Detailed explanation of the knowledge of ToPrimitive abstract operation in ECMAScript7 specification (example). For more information, please follow other related articles on the PHP Chinese website!