JS对象与原始值的转换机制

我们都知道原始值之间是可以互相转换的,但是如果对象转原始值呢?

  • 所有的对象在布尔上下文(context)中均为 true 。所以对于对象,不存在 to-boolean 转换, 只有字符串和数值转换。
  • 数值转换发生在对象相减或应用数学函数时。例如, Date 对象(将在 日期和时间 一章中介 绍)可以相减, date1 - date2 的结果是两个日期之间的差值。
  • 至于字符串转换 —— 通常发生在我们像 alert(obj) 这样输出一个对象和类似的上下文中。

所以,由此可见对象转原始值的情况下大致可以分为数值转换和字符串转换两种,继续往下

什么是hint?

7.1.1.1 OrdinaryToPrimitive ( <var>O</var>, <var>hint</var> )

The abstract operation OrdinaryToPrimitive takes arguments <var>O</var> and <var>hint</var>. It performs the following steps when called:

  1. Assert: Type(<var>O</var>) is Object.

  2. Assert: <var>hint</var> is either string or number.

  3. If <var>hint</var> is string, then

    1. Let <var>methodNames</var> be « "toString", "valueOf" ».
  4. Else,

    1. Let <var>methodNames</var> be « "valueOf", "toString" ».
  5. For each element <var>name</var> of <var>methodNames</var>, doThrow a TypeError exception.

    1. Let <var>method</var> be ? Get(<var>O</var>, <var>name</var>).
    2. If IsCallable(<var>method</var>) is true, then
      1. Let <var>result</var> be ? Call(<var>method</var>, <var>O</var>).
      2. If Type(<var>result</var>) is not Object, return <var>result</var>.

我们在ECMA规范中可以得知,hint是类型转换的变体,不太明白?没关系,继续往下

当在对象到字符串的转换中,hint的值便是"string"

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var obj = {name:"谢绝先生"}; var anotherObj = {[obj]:"北有极光"};</pre>

当在对象到数字的转换中,hint的值便是"number"

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var user = {name:"申屠肆"};
console.log(+user);</pre>

对于不确定转换的类型时,它将依据 hint的值为"default" ,例如,二进制加法 + 可用于字符串(连接),也可以用于数字(相加),所以字符串和数字这两 种类型都可以

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var sth = {name:"申屠肆"};
console.log(sth + 2);</pre>

接下来,更进一步,为了进行转换,JavaScript 尝试查找并调用三个对象方法:

  1. Symbol.toPrimitive
  2. toString
  3. valueOf

在进行类型转换的时候,首先会去查找个名为 Symbol.toPrimitive 的内建 symbol,像这样

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">obj[Symbol.toPrimitive] = function(hint) { // 返回一个原始值
// hint = "string"、"number" 和 "default" 中的一个
}</pre>

该函数接收一个参数 hint,hint便是需要进行转换的原始值的类型;通过该函数我们可以完全掌控生成什么样的原始值,从而达到我们想要的目的,举个栗子:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var sth = {
name:"申屠肆",
money:1000,
Symbol.toPrimitive { if (hint == "string") return this.name; else
return this.money;
}
}

console.log(+sth); // 1000
console.log(${sth}); // 申屠肆</pre>

古老的 toString / valueOf

如果没有 Symbol.toPrimitive ,那么 JavaScript 将尝试找到它们,并且按照下面的顺序进行 尝试: 对于 “string” hint, toString -> valueOf 。 其他情况, valueOf -> toString 。 这些方法必须返回一个原始值。如果 toString 或 valueOf 返回了一个对象,那么返回值会 被忽略(和这里没有方法的时候相同)。 默认情况下,普通对象具有 toString 和 valueOf 方法: toString 方法返回一个字符串 "[object Object]" 。 Symbol.toPrimitive obj[Symbol.toPrimitive] = function(hint) { // 返回一个原始值 // hint = "string"、"number" 和 "default" 中的一个 } let user = { name: "John", money: 1000, Symbol.toPrimitive { alert(hint: ${hint}); return hint == "string" ? {name: "${this.name}"} : this.money; } }; // 转换演示: alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500 toString/valueOf ● valueOf 方法返回对象自身。

解释一下:

就是说如果hint 为 "string",在没有 Symbol.toPrimitive 的情况下,会优先查找 toString方法;

其他情况下("default","number"),在没有 Symbol.toPrimitive 的情况下,valueOf的优先级高一些;

至于为什么valueOf和toString方法返回的只能是原始值的情况下,valueOf还会返回对象本身,这是一个历史问题;

代码实例:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">//Node.js环境下,根目录下 index.js文件
let user = {
name:'Joe',
money:1000,
toString() { // "hint" 为 string
console.log('执行执行~',this.name); return this.name;
},
valueOf() { // hint 为 default 或者 number;
return this.money;
}
}

module.exports = user;</pre>

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">//与index.js文件同目录下的测试文件

var assert = require('chai').assert; var user = require('./index');

describe('对象转原始值',function() {
it('hint 为 string的情况下',function() {
assert( ${user} === user.name,'成功的触发了toString方法');
});
it('hint 为 default的情况下',function() {
assert( user + 1 === user.money + 1,'成功的触发了valueOf方法');
});
it('hint 为 number的情况下',function() {
assert( +user === user.money,'成功的触发了valueOf方法');
});
})</pre>

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">// package.json
{ "name": "something", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "test":"mocha ./index.test.js" }, "dependencies": { "chai": "^4.3.0", "mocha": "^8.2.1" }
}</pre>

单元测试结果如下:

image

2021-02-04 23:56:54
我的博客园地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容