You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
使 result 等于 Call(exoticToPrim, input, « hint ») 的结果(大致意思是,执行 @@toPrimitive 方法,即 exoticToPrim(hint))。
如果 result 不是引用类型,则返回 result。
否则抛出 TypeError 类型错误。
如果 hint 是 "default",则将 hint 设为 "number"。
返回 OrdinaryToPrimitive(input, hint)。
返回 input(即原始类型的值直接返回)。
注意:当不带 hint 去调用 ToPrimitive 抽象操作时,通常它的行为就像 hint 是 Number 类型一样。但是,(派生)对象可以通过定义 @@toPrimitive 方法来替代此行为。在本规范中定义的对象里,只有 Date 对象(详情看 #sec-20.4.4.45)和 Symbol 对象(详情看 #sec-19.4.3.5)会覆盖默认的 ToPrimitive 行为。
关于 Date 对象的 Date.prototype[@@toPrimitive] 内部方法实现,其实是将 hint 为 "default" 的情况改为 "string",然后执行第 5 步的 OrdinaryToPrimitive 操作。(详情看 #sec-20.4.4.45)
关于 Symbol 对象的 Symbol.prototype[@@toPrimitive] 内部方法实现,如果传递给该方法的是一个 Symbol 类型的值,则直接返回该值。如果该值是引用类型,且含有属性 [[SymbolData]],而且该属性值为 Symbol 类型,则返回该属性值,否则会抛出 TypeError 类型错误。(详情看 #sec-19.4.3.5)
// 运算符: x + y// Number + Number -> 数字相加1+2// 3// Boolean + Number -> 数字相加true+1// 2// Boolean + Boolean -> 数字相加false+false// 0// Number + String -> 字符串连接5+'foo'// "5foo"// String + Boolean -> 字符串连接'foo'+false// "foofalse"// String + String -> 字符串连接'foo'+'bar'// "foobar"
constobj={[Symbol.toPrimitive]: hint=>{if(hint==='number'){return1}elseif(hint==='string'){return'string'}else{return'default'}}}+obj// 1 hint is "number"`${obj}`// "string" hint is "string"obj+''// "default" hint is "default"obj+1// "default1" hint is "default"Number(obj)// 1 hint is "number"String(obj)// "string" hint is "string"
// 下面会导致 Symbol('foo') 进行隐式转换,即 ToString(Symbol),按以上规则,是会抛出异常的console.log(Symbol('foo')+'bar')// TypeError: Cannot convert a Symbol value to a string// Symbol('foo') 结果是 Symbol 的原始值,再调用其包装对象的属性时,会自动转化为包装对象再调用其 toString() 方法console.log(Symbol('foo').toString()+'bar')// "Symbol(foo)bar"
抛一个有趣的问题:
// 运行出错varname=Symbol()// TypeError: Cannot convert a Symbol value to a string// 正常运行,不会抛出错误letname=Symbol()// 为什么呢 ❓❓❓
需要注意都是,JavaScript 内置的 Symbol、BigInt 对象不能使用 new 关键字去创建实例对象,只能通过 Object() 函数来创建一个包装对象(wrapper object)。
// 错误示例constsym=newSymbol()// TypeError: Symbol is not a constructor// 正确示例constsym=Symbol()console.log(typeofsym)// "symbol"constsymObj=Object(sym)console.log(typeofsymObj)// "object"// BigInt 同理
需要注意的是,从 ES6 开始围绕原始数据类型创建一个显式包装器对象不再被支持。但由于遗留原因,现有的原始包装器对象(如 new Boolean、new Number、new String)仍可使用。这也是 ES6+ 新增的 Symbol、BigInt 数据类型无法通过 new 关键字创建实例对象的原因。
在 JavaScript 的世界里,数据类型之间的转换无处不在。即使你没有主动显式地去转换,但 JavaScript 在私底下“偷偷地”帮我们做了很多类型转换的工作。那么,它们究竟是按照什么规则去转换的呢?我们试图在本文中找到答案,来解开谜底。
ECMAScript 是负责制定标准的,而 JavaScript 则是前者的一种实现。
在 ECMAScript 标准中定义一组转换抽象操作,常见的抽象操作(Abstract Operation)有
ToPrimitive
、ToBoolean
、ToNumber
、ToString
、ToObject
等等。一、ToPrimitive
1. ToPrimitive
在 ECMAScript 标准中,使用 ToPrimitive 抽象操作将引用类型转换为原始类型。(详情看 #sec-7.1.1)
参数
input
是文章开头提到的 8 种数据类型的值(Undefined、Null、Boolean、String、Symbol、Number、BigInt、Object)。参数PreferredType
是可选的,表示要转换到的原始值的预期类型,取值只能是字符串"default"
(默认)、"string"
、"number"
之一。ToPrimitive 操作,可概括如下:
input
是 ECMAScript 语言类型的值。input
是引用类型,那么PreferredType
参数,使hint
等于"default"
。PreferredType
提示 String 类型,使hint
等于"string"
。PreferredType
提示 Number 类型,使hint
等于"number"
。exoticToPrim
等于GetMethod(input, @@toPrimitive)
的结果(大致意思是,获取input
对象的@@toPrimitive
属性值,并将其赋给exoticToPrim
)。exoticToPrim
不等于undefined
(即input
对象含@@toPrimitive
属性),那么result
等于Call(exoticToPrim, input, « hint »)
的结果(大致意思是,执行@@toPrimitive
方法,即exoticToPrim(hint)
)。result
不是引用类型,则返回result
。TypeError
类型错误。hint
是"default"
,则将hint
设为"number"
。OrdinaryToPrimitive(input, hint)
。input
(即原始类型的值直接返回)。用口水话再总结一下,如下(哈哈):
input
是原始类型,直接返回input
(不做转换操作)。PreferredType
是 String(Number)类型,那么使得hint
等于"string"
("number"
),否则hint
等于默认的"default"
。input
中存在@@toPrimitive
属性(方法),若@@toPrimitive
方法的返回值为原始类型,则 ToPrimitive 的操作结果就是该返回值,否则抛出TypeError
类型错误。hint
是"default"
,则使hint
等于"number"
。OrdinaryToPrimitive(input, hint)
操作的结果。那么
OrdinaryToPrimitive
的操作是怎样的呢?我们接着往下看...2. OrdinaryToPrimitive
详情看:#sec-7.1.11
参数
O
为引用类型。参数hint
为 String 类型,其值只能是字符串"string"
、"number"
之一。(官话)OrdinaryToPrimitive 操作,可概括如下:
O
是引用类型。hint
是 String 类型,且hint
的值只能是"string"
或"number"
之一。hint
为"string"
,使methodNames
等于« "toString", "valueOf" »
(其中«»
表示规范中的 List,类似于数组)。hint
为"number"
,使methodNames
等于« "valueOf", "toString" »
。methodNames
,使name
等于每个迭代值,并执行:method
等于Get(O, name)
(即获取对象O
的name
属性,相当于获取对象的toString
或valueOf
属性,具体执行顺序视hint
而定)。IsCallable(method)
结果为true
,那么:result
等于Call(method, O)
结果(即调用method()
方法)。result
为原始类型,则返回result
。TypeError
类型错误。(口水话)再总结一下:
OrdinaryToPrimitive(input, hint)
操作,那么步骤如下:hint
为"string"
,它会先调用input.toString()
方法,toString()
结果为原始类型,则直接返回该结果。input.valueOf()
方法,若结果为原始类型,则返回该结果,否则抛出TypeError
类型错误。hint
为"number"
,它先调用input.valueOf()
方法,valueOf()
结果为原始类型,则直接返回该结果。input.toString()
方法,若结果为原始类型,则返回该结果,否则抛出TypeError
类型错误。3. 一些示例
-
、*
、/
、%
这四种操作符都会把符号两边的操作数先转换为数字再进行运算。+
的作用可以是数值求和,也可以是字符串拼接。二、ToBoolean
将一个操作数转换为布尔值,这应该是最简单的了。(详情看 #sec-7.1.2)
所以总结下来就是:
undefined
、null
、false
、+0
、-0
、NaN
、''
、0n
在 JavaScript 中,如果一个操作数
argument
通过ToBoolean(argument)
操作后被转换为true
,那么这些操作数称为真值(truthy),否则为虚值(falsy)。三、ToNumber
将一个操作数转换为数字值。(详情看 #sec-7.1.4)
NaN
+0
true
转换为1
,false
转换为+0
。2. 空字符串
''
转为+0
;3. 否则为
NaN
;其中
0x
开头的字符串被当成 16 进制。TypeError
错误。TypeError
错误。1. 将引用类型转化为原始值
ToPrimitive(argument, 'number')
;2. 转化为原始值后,进行
ToNumber(primValue)
操作,即按上面的类型转换。四、ToString
将一个操作数转换为字符串类型的值。(详情看 #sec-7.1.17)
undefined
null
true
转换为"true"
,false
转换为"false"
。NaN
转换为"NaN"
;2.
+0
或-0
转换为"0"
;3. 其中
Infinity
和-Infinity
分别转换为"Infinity"
、"-Infinity"
;4. 若
x
是小于0
的负数,则返回"-x"
;若x
是大于0
的正数,则返回"x"
;5. 其他不常用的数值,请看 #sec-6.1.6.1.20。
TypeError
错误。10n
转换为"10"
。1. 将引用类型转化为原始值
ToPrimitive(argument, 'string')
;2. 转化为原始值后,进行
ToString(primValue)
操作,即按上面的类型转换。抛一个有趣的问题:
五、ToObject
将一个操作数转换为引用类型的值。(详情看 #sec-7.1.18)
TypeError
错误。TypeError
错误。new Boolean(argument)
。new Number(argument)
。new String(argument)
。Object(Symbol(argument))
。Object(BigInt(argument))
。六、参考
The text was updated successfully, but these errors were encountered: