本文修改自本人之前写的文章。
从范例提及
js只需7种范例:
原始范例(primitives types)
- boolean
number
- 包含Infinity和NaN,你能够经由历程
typeof Infinity;
来考证
- 包含Infinity和NaN,你能够经由历程
- string
- null
- undefined
- Symbol (ECMAScript 6 新定义,暂时用不上,这篇文章不议论)
Object 范例
- js内置了很多对象供你运用,MDN文档列举了一切ES范例定义的内置对象。
js的自动装箱
虽然string是原始范例,但为何我们彷佛能够挪用“string的函数”呢?
var str = 'I am str';
str.toUpperCase(); // "I AM STR"
原因是js范例库给boolean、number、string离别供应了包装范例:Boolean、Number、String。在须要的时刻,原始范例会自动转换成响应的包装对象(这个历程叫自动装箱)。上例的toUpperCase
就是String范例对象定义的一个函数。
自动装箱就是暂时建立一个包装对象,将原始范例的值封装起来,以便挪用包装对象的函数。然则本来谁人变量的值不会有任何变化!实行完上面例子的代码今后,str指向的依然是谁人原始值:
typeof str; // "string"
固然,你能够将Boolean 、Number 、String 这三个函数看成组织函数来运用,经由历程手动new包装类来装箱(获得包装对象):
var str_object = new String('I am str_object'); // 手动装箱
str_object.toUpperCase(); // "I AM STR_OBJECT"
typeof str_object; // "object"
这三个函数除了能看成组织函数,还能作为一般函数来挪用(获得对应的原始范例值)。在文章的背面,我们会将这三个函数看成一般的函数运用,完成强迫范例转换。
两个与范例转换有关的函数:valueOf()和toString()
- valueOf()的语义是,返回这个对象逻辑上对应的原始范例的值。比方说,String包装对象的valueOf(),应当返回这个对象所包装的字符串。
- toString()的语义是,返回这个对象的字符串示意。用一个字符串来形貌这个对象的内容。
valueOf()和toString()是定义在Object.prototype上的要领,也就是说,一切的对象都邑继续到这两个要领。然则在Object.prototype上定义的这两个要领每每不能满足我们的需求(Object.prototype.valueOf()仅仅返回对象本身),因而js的很多内置对象都重写了这两个函数,以完成更适合本身的功用须要(比方说,String.prototype.valueOf就掩盖了在Object.prototype中定义的valueOf)。当我们自定义对象的时刻,最好也重写这个要领。重写这个要领时要遵照上面所说的语义。
以下是部份内置对象挪用valueOf()的行动:
对象 | 返回值 |
---|---|
Array | 数组本身(对象范例)。 |
Boolean | 布尔值(原始范例)。 |
Date | 从 UTC 1970 年 1 月 1 日半夜最先盘算,到所封装的日期所经由的毫秒数(原始范例)。 |
Function | 函数本身(对象范例)。 |
Number | 数字值(原始范例)。 |
Object | 对象本身(对象范例)。假如自定义对象没有重写valueOf要领,就会运用它。 |
String | 字符串值(原始范例)。 |
由上表可见,valueOf()虽然希冀返回原始范例的值,然则现实上有一些对象在逻辑上没法找到与之对应的原始值,因而只能返回对象本身。
toString()则不一样,由于不管什么对象,我们总有方法“形貌”它,因而js内置对象的toString()总能返回一个原始string范例的值。
var d = new Date();
d.toString()
// "Fri Apr 21 2017 14:54:04 GMT+0800 (中国范例时间)"
我们本身在重写toString()的时刻也应当返回合理的string。
valueOf()和toString()常常会在范例转换的时刻被js内部挪用,比方说我们后文会谈到的ToPrimitive。在自定义对象上合理地掩盖valueOf()和toString(),能够掌握自定义对象的范例转换。
js内部用于完成范例转换的4个函数
这4个要领现实上是ECMAScript定义的4个笼统的操纵,它们在js内部运用,举行范例转换。我们js的运用者不能直接挪用这些函数,然则相识这些函数有利于我们明白js范例转换的道理。
- ToPrimitive ( input [ , PreferredType ] )
- ToBoolean ( argument )
- ToNumber ( argument )
- ToString ( argument )
请辨别这里的ToString()和上文谈到的toString(),一个是js引擎内部运用的函数,另一个是定义在对象上的函数。
ToPrimitive ( input [ , PreferredType ] )
将input转化成一个原始范例的值。PreferredType参数要么不传入,要么是Number 或 String。假如PreferredType参数是Number,ToPrimitive如许实行:
- 假如input本身就是原始范例,直接返回input。
- 挪用input.valueOf(),假如效果是原始范例,则返回这个效果。
- 挪用input.toString(),假如效果是原始范例,则返回这个效果。
- 抛出TypeError非常。
以下是PreferredType不为Number时的实行递次。
- 假如PreferredType参数是String,则交流上面这个历程的第2和第3步的递次,其他实行历程雷同。
假如PreferredType参数没有传入
- 假如input是内置的Date范例,PreferredType 视为String
- 不然PreferredType 视为 Number
能够看出,ToPrimitive依赖于valueOf和toString的完成。
ToBoolean ( argument )
Argument Type | Result |
---|---|
Undefined | Return false |
Null | Return false |
Boolean | Return argument |
Number | 仅当argument为 +0, -0, or NaN时, return false; 不然一概 return true |
String | 仅当argument是空字符串(长度为0)时, return false; 不然一概 return true |
Symbol | Return true |
Object | Return true |
表格来自ECMAScript范例。
只须要影象0, null, undefined, NaN, ""
返回false就能够了,其他一概返回true。
ToNumber ( argument )
Argument Type | Result |
---|---|
Undefined | Return NaN |
Null | Return +0 |
Boolean | 假如 argument 为 true, return 1. 假如 argument 为 false, return +0 |
Number | 直接返回argument |
String | 将字符串中的内容转化为数字(比方”23″->23),假如转化失利则返回NaN(比方”23a”->NaN) |
Symbol | 抛出 TypeError 非常 |
Object | 先primValue = ToPrimitive(argument, Number),再对primValue 运用 ToNumber(primValue) |
由上表可见ToNumber的转化并不老是胜利,偶然会转化成NaN,偶然则直接抛出非常。
ToString ( argument )
Argument Type | Result |
---|---|
Undefined | Return “undefined” |
Null | Return “null” |
Boolean | 假如 argument 为 true, return “true”.假如 argument 为 false, return “false” |
Number | 用字符串来示意这个数字 |
String | 直接返回 argument |
Symbol | 抛出 TypeError 非常 |
Object | 先primValue = ToPrimitive(argument, hint String),再对primValue运用ToString(primValue) |
隐式范例转换(自动范例转换)
当js希冀获得某种范例的值,而现实在那里的值是其他的范例,就会发作隐式范例转换。体系内部会自动挪用我们前面说ToBoolean ( argument )、ToNumber ( argument )、ToString ( argument ),尝试转换成希冀的数据范例。
例子1:
if ( !undefined
&& !null
&& !0
&& !NaN
&& !''
) {
console.log('true');
} // true
例子1:由于在if的括号中,js希冀获得boolean的值,所以对括号中每个值都运用ToBoolean ( argument ),将它们转化成boolean。
例子2:
3 * { valueOf: function () { return 5 } }; //15
例子2:由于在乘号的两头,js希冀获得number范例的值,所以对右侧的谁人对象运用ToNumber ( argument ),获得效果5,再与乘号左侧的3相乘。
例子3:
> function returnObject() { return {} }
> 3 * { valueOf: function () { return {} }, toString: function () { return {} } }
// TypeError: Cannot convert object to primitive value
例子3:挪用ToNumber ( argument )的历程当中,挪用了ToPrimitive ( input , Number ),由于在ToPrimitive中valueOf和toString都没有返回原始范例,所以抛出非常。
标记’+’是一个比较辣手的一个标记,由于它既能够示意“算数加法”,也能够示意“字符串拼接”。
简朴明白版本:只需’+’两头的恣意一个操纵数是字符串,那末这个’+’就示意字符串拼接,不然示意算数加法。
12+3
// 15
12+'3'
// "123"
道理明白版本:依据ECMAScript的定义,对’+’运算的求值根据以下历程:
- 令lval = 标记左侧的值,rval = 标记右侧的值
- 令lprim = ToPrimitive(lval),rprim = ToPrimitive(rval)
- 假如lprim和rprim中有恣意一个为string范例,将ToString(lprim)和ToString(rprim)的效果做字符串拼接
- 不然,将ToNumber(lprim)和ToNumber(rprim)的效果做算数加法
依据这个道理能够诠释
[]+[]
// ""
// 提醒:ToPrimitive([])返回空字符串
[] + {}
// "[object Object]"
// 提醒:ToPrimitive({})返回"[object Object]"
123 + { toString: function () { return "def" } }
// "123def"
// 提醒:ToPrimitive(加号右侧的对象)返回"def"
{} + []
// 0
// 效果不相符我们的预期:"[object Object]"
// 提醒:在Chrome中,标记左侧的{}被诠释成了一个语句块,而不是一个对象
// 注意在别的实行引擎上能够会将{}诠释成对象
// 这一行等价于'+[]'
// '+anyValue'等价于Number(anyValue)
({}) + []
// "[object Object]"
// 加上括号今后,{}被诠释成了一个对象,效果相符我们的预期了
‘<‘、’>’的状况与’+’相似,然则处理方式与’+’有些差别。假如猎奇请自行查阅
文档。
显式范例转换(强迫范例转换)
程序员显式挪用Boolean(value)、Number(value)、String(value)完成的范例转换,叫做显现范例转换。
我们在文章的前面说过new Boolean(value)、new Number(value)、new String(value)传入各自对应的原始范例的值,能够完成“装箱”——将原始范例封装成一个对象。实在这三个函数不单单议能够看成组织函数,它们能够直接看成一般的函数来运用,将任何范例的参数转化成原始范例的值:
Boolean('sdfsd'); // true
Number("23"); // 23
String({a:24}); // "[object Object]"
实在这三个函数用于范例转换的时刻,挪用的就是js内部的ToBoolean ( argument )、ToNumber ( argument )、ToString ( argument )
要领!
这里诠释一下String({a:24}); // "[object Object]"
的历程:
实行String({a:24})
实行js内部函数ToString ( {a:24} )
实行
primValue = ToPrimitive({a:24}, hint String)
- 由于{a:24}不是原始范例,进入下一步。
- 在ToPrimitive内挪用({a:24}).toString(),返回了原始值”[object Object]”,因而直接返回这个字符串,ToPrimitive背面的步骤不必举行下去了。
- primValue被赋值为ToPrimitive的返回值:”[object Object]”
- 实行js内部函数ToString ( “[object Object]” ),返回”[object Object]”
- 返回”[object Object]”
- 返回”[object Object]”
- 返回”[object Object]”
为了防备涌现意料之外的效果,
最幸亏不确定的处所运用显式范例转换。
参考文章:
ECMAScript范例转换范例
Object to primitive conversion
What is {} + {} in JavaScript?
JavaScript quirk 1: implicit conversion of values
Object.prototype.toString()的道理 – ECMAScript
转变Object.prototype.toString.call(myClass)的输出
比较操纵符的范例转换