JavaScript范例转换道理

本文修改自本人之前写的文章

从范例提及

js只需7种范例:

  • 原始范例(primitives types)

    • boolean
    • number

      • 包含Infinity和NaN,你能够经由历程typeof Infinity;来考证
    • 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离别供应了包装范例:BooleanNumberString。在须要的时刻,原始范例会自动转换成响应的包装对象(这个历程叫自动装箱)。上例的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如许实行:

  1. 假如input本身就是原始范例,直接返回input。
  2. 挪用input.valueOf(),假如效果是原始范例,则返回这个效果。
  3. 挪用input.toString(),假如效果是原始范例,则返回这个效果。
  4. 抛出TypeError非常。

以下是PreferredType不为Number时的实行递次。

  • 假如PreferredType参数是String,则交流上面这个历程的第2和第3步的递次,其他实行历程雷同。
  • 假如PreferredType参数没有传入

    • 假如input是内置的Date范例,PreferredType 视为String
    • 不然PreferredType 视为 Number

能够看出,ToPrimitive依赖于valueOf和toString的完成。

ToBoolean ( argument )

Argument TypeResult
UndefinedReturn false
NullReturn false
BooleanReturn argument
Number仅当argument为 +0, -0, or NaN时, return false; 不然一概 return true
String仅当argument是空字符串(长度为0)时, return false; 不然一概 return true
SymbolReturn true
ObjectReturn true

表格来自ECMAScript范例
只须要影象0, null, undefined, NaN, ""返回false就能够了,其他一概返回true。

ToNumber ( argument )

Argument TypeResult
UndefinedReturn NaN
NullReturn +0
Boolean假如 argument 为 true, return 1. 假如 argument 为 false, return +0
Number直接返回argument
String将字符串中的内容转化为数字(比方”23″->23),假如转化失利则返回NaN(比方”23a”->NaN)
Symbol抛出 TypeError 非常
ObjectprimValue = ToPrimitive(argument, Number),再对primValue 运用 ToNumber(primValue)

由上表可见ToNumber的转化并不老是胜利,偶然会转化成NaN,偶然则直接抛出非常。

ToString ( argument )

Argument TypeResult
UndefinedReturn “undefined”
NullReturn “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的定义,对’+’运算的求值根据以下历程:

  1. 令lval = 标记左侧的值,rval = 标记右侧的值
  2. 令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)

        1. 由于{a:24}不是原始范例,进入下一步。
        2. 在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)的输出
比较操纵符的范例转换

    原文作者:csRyan
    原文地址: https://segmentfault.com/a/1190000016262869
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞