文章目录
对象-软件系统定制开发软件系统定制开发基础类型转换
软件系统定制开发当我们在对象上使用数软件系统定制开发学运算符时会发生什么?例如obj1 + obj2
,软件系统定制开发亦或者使用console.log(obj)
软件系统定制开发会输出什么内容?软件系统定制开发这操作的结果都取决于对象-基础类型转换的结果。
本文目的:
- 软件系统定制开发了解对象参与数学运算软件系统定制开发时发生了什么;
- 软件系统定制开发了解对象数学运算的例软件系统定制开发外情况并利用;
写在前面
软件系统定制开发在无所不能的C++
中,软件系统定制开发我们能够通过一种非常BUG
的方式(运算符重载)自定义对象的各种运算符功能。遗憾的是JavaScript
不支持自定义运算符实现对象运算,在参与运算时,对象会被隐式的转为基础类型,然后对这些基础类型数据进行运算,最后返回运算结果。
例如,我们没有办法直接对两个表示矩阵的对象执行,然后得到矩阵运算的结果,这种行为是走不通的。(补充,这种行为在C++
中易如反掌)
由于JavaScript
本身不支持运算符重载,如果你在代码中发现了对象的数学运算,大部分情况下是错误的操作。
对象类型转换规则
在前文的章节,我们已经详细的了解了基础类型之间的相互转换规则,但是我们并没有涉及对象的类型转换,这正是本文的重点内容:
- 不会转为布尔类型。对象隐式转换只能转为字符串和数字,如果我们主动转换对象为布尔类型,只会得到
true
,不论对象是否为空; - 转为数字类型发生在对象相减或者应用数学函数时,例如,
Date
对象相减会得到两个日期之间的差值; - 转为字符串通常发生在诸如
alert(obj)
或者对象属性键这种输出函数中。
以上转换并非完全固定的,JavaScript
允许我们使用特殊的对象方法,自己实现字符串和数字之间的转换。
对象类型转换的结果 Hint
JavaScript
如何决定对象转换为何种类型的数据呢? 这取决于hint
的取值:
String
如果hint
的取值为"string"
,就会将对象转为字符串,常发生在以下情况:
//作为字符串输出alert(obj);console.log(obj);//作为属性键Obj1[obj] = 996;
- 1
- 2
- 3
- 4
- 5
Number
当hint
的取值为"number"
时,对象会转为数字类型,这通常发生在数学运算、比较运算情况下:
let num = Number(obj);//显式转换//除了二元加法的数学运算let x = +obj;//一元加法(和显式转换效果相同)let y = date1 - date2;//比较运算符let bigger = num1 > num2;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Default
当hint
取值为"default"
时,JavaScript
引擎也不确定将对象转为何种类型。
例如,我们使用二元加法+
操作对象,由于字符串和数字均可作二元加法,不确定具体使用哪一种方法,就以"default"
进行转换。
亦或者,我们使用比较运算符==
操作对象时,对象应该转为何种类型也不是很明确,因此使用"default"
进行转换。
let n = obj1 + obj2;//二元加if(obj == 996){//比较运算 ...}
- 1
- 2
- 3
- 4
注意:
比较运算符
<
和>
虽然同样可以比较字符串和数字,但是由于历史原因,导致使用number
而不是default
。
转换需要的三个对象方法
为了将对象转为我们需要的类型,引擎通常需要操作三个对象方法:
- 如果方法存在,调用
obj[Symbol.toPrimitive](hint)
方法,直接将对象转为我们需要的类型,其中Symbo.toPrimitive
是系统symbol
方法; - 否则,如果
hint
是"string"
,调用obj.toString()
或者obj.valueOf()
; - 否则,如果
hint
是"number"
,调用obj.valueOf()
或者obj.toString()
;
Symbol.toPrimitive()
有一个名为Symbol.toPrimitive
的内建Symbol
用来命名对象的类型转换方法,实现方法如下:
obj[Symbol.toPrimitive] = function(hint){ //对象转为hint类型的代码 //hint有三种取值string、number、default}
- 1
- 2
- 3
- 4
正如上面展示的引擎调用类型转换方法的顺序,如果对象存在Symbol.toPrimitive
方法,就不需要其他类型转换方法了。
举个栗子:
let people = { name : 'xiaoming', age : 33, [Symbol.toPrimitive](hint){ console.log(`hint:${hint}`); return hint == "string" ? `{name:"${this.name}",age:${this.age}}`:this.age; }}alert(people);//隐式转为String类型console.log(+people);//转为Number类型console.log(people+1);//转为Number类型
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
以上代码,首先会弹出一个对话框如下图:
这是因为alert(people)
会调用Symbol.toPrimitive(hint)
方法,其中hint
的值为"string"
。
随后代码执行结果的输出如下:
对象people
分别被转为了"number"
和"default"
,由于我们写的代码非常简单,并没有区分二者的区别,因此都是以数字的方式展现的。
上述案例可以发现,对象在不同的需求场景下,会通过hint
参数的取值不同更改转换的最终结果,从而适应代码的执行。
toString和valueOf方法
以上Symbol.toPrimitive(hint)
方法是一个通用的对象类型转换方法,但是,如果对象中没有定义这个方法,那么就需要寻找并使用toString
和valueOf
方法进行类型转换。
二者调用的区别如下:
- 如果
hint
的值是"string"
,调用toString
方法,如果找不到,就调用valueOf
方法; - 如果
hint
的不是"string"
,调用valueOf
方法,如果找不到,就调用toString
方法;
以上规则暗藏了一个特性,就是数学运算符会优先调用valueOf
方法。
toString
和valueOf
是一种以约定名称的方式实现类型转换的方式,两个函数都是常规字符串命名的方法,属于一种“复古”风格的方法。
注意:
toString
和valueOf
方法都必须返回一个基本类型的返回值,如果返回一个对象,那么返回值就会被忽略,和没有找到这个方法的情况相同。
默认情况下,对象都具备一个默认的toString
和valueOf
方法:
toString
返回一个字符串"[object Object]"
;valueOf
返回对象本身;
相信很多同学都有意无意的使用alert
函数展示过对象的数据,最终显示的都是字符串[object Object]
。
举例如下:
let people = { name : "xiaoming"};alert(people);//展示[object Object]alert(people.valueOf() === people);//弹出true
- 1
- 2
- 3
代码执行结果如下图所示:
提示:
这里涉及到一个面向对象的知识,这些对象默认的方法都是当前对象继承自父类的,这些知识会在后面的面向对象里涉及到,这里不用奇怪这些函数是从哪里来的。
这里,我们也可以重写toString
和valueOf
方法,从而实现我们对对象类型转换的控制。
举例如下:
let xiaoming = { name : "xiaoming", age : 12, toString() {//hint 为 string return `{ name : ${this.name},age : ${this.age} }`; }, valueOf() {//hint 为 number或default return this.age; }};alert(xiaoming);//调用toStringalert(+xiaoming);//调用valueOf
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
代码执行结果:
由于
toString
和valueOf
方法可以通过重写完成我们的意愿,实际上只要返回一个基础类型即可,我们没有必要按照hint
的指示返回对应的类型。我们可以返回任意类型而不出任何错误,但是并不建议这么做。
toString的神秘使命
如果一个对象只有一个toString
方法,那么它将承担所有的类型转换工作,这是toString
函数的神秘使命。
举个例子:
let xiaoming = { toString(){ return '996'; }}console.log(xiaoming * 2);
- 1
- 2
- 3
- 4
- 5
- 6
上述代码中的xiaoming
对象,只有一个toString
函数能进行类型转换,如果我们对它进行乘法运算,默认情况下应该转为数字类型,但由于没有valueOf
方法,只能让toString
执行转换的任务。
代码执行结果如下:
代码执行过程中发生了一些我们预测之外的事情:
- 使用
toString
代替valueOf
返回一个字符串"996"
; - 字符串
"996"
会再次隐式转换为数字996
; - 最后参与运算;
如果把乘法换为加法,就会发生其他的转换:
let xiaoming = { toString(){ return '996'; }}console.log(xiaoming + 2);
- 1
- 2
- 3
- 4
- 5
- 6
代码的执行结果如下:
二元加法优先接收字符串,因此这种情况下没有数字的转换,直接转为字符串拼接。
总结
-
转换类型有三种(
hint
):string
、number
、default
; -
obj[Symbol.toPrimitive](hint)
方法可以转任何类型,如果不存在,执行下一条; -
如果
hint
为"string"
,使用toString
方法,找不到就使用valueOf
; -
如果
hint
不是"string"
,优先使用valueOf
,不存在调用toString
; -
toString
方法啥都能干,实际使用中,我们最常用这个;