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

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

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

什么是hint?

7.1.1.1 OrdinaryToPrimitive ( Ohint )

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

  1. AssertType(O) is Object.

  2. Asserthint is either string or number.

  3. If hint is string, then

    1. Let methodNames be « "toString", "valueOf" ».
  4. Else,

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

    1. Let method be ? Get(Oname).
    2. If IsCallable(method) is true, then
      1. Let result be ? Call(methodO).
      2. If Type(result) is not Object, return result.

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

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

var obj = {name:"谢绝先生"};
var anotherObj = {[obj]:"北有极光"};

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

var user = {name:"申屠肆"};
console.log(+user);

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

var sth = {name:"申屠肆"};
console.log(sth + 2);

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

  1. Symbol.toPrimitive
  2. toString
  3. valueOf

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

obj[Symbol.toPrimitive] = function(hint) {
// 返回一个原始值
// hint = "string"、"number" 和 "default" 中的一个
}

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

var sth = {
name:"申屠肆",
money:1000,
[Symbol.toPrimitive](hint) {
if (hint == "string")
return this.name;
else
return this.money;
}
} console.log(+sth); // 1000
console.log(`${sth}`); // 申屠肆

古老的 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](hint) { 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还会返回对象本身,这是一个历史问题;

代码实例:

//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;
//与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方法');
});
})
// 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"
}
}

单元测试结果如下:

2021-02-04 23:56:54

深入剖析JavaScript中的对象与原始值之间的转换机制的更多相关文章

  1. JavaScript中字符串与16进制之间的转换

    一.字符串转换为16进制 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  2. JavaScript中对象转换为原始值的规则

    JavaScript中对象转换为原始值遵循哪些原则? P52 对象到布尔值对象到布尔值的转换非常简单:所有的对象(包括数字和函数)都转换为true.对于包装对象亦是如此:new Boolean(fal ...

  3. javascript中的对象,原型,原型链和面向对象

    一.javascript中的属性.方法 1.首先,关于javascript中的函数/“方法”,说明两点: 1)如果访问的对象属性是一个函数,有些开发者容易认为该函数属于这个对象,因此把“属性访问”叫做 ...

  4. JavaScript中判断对象类型方法大全1

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  5. JavaScript中判断对象类型的种种方法

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  6. 转 JavaScript中判断对象类型的种种方法

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  7. javascript中字符串对象常用的方法和属性

    前言 字符串是一种非常重要的数据类型,在Java等面向对象编程语言中,它代表对象类型,而在javascript中它却是一种基本数据类型,在开发的领域中,我们经常会碰到,无论是前端还是后台.比如后台验证 ...

  8. Javascript 中判断对象为空

    发现了一个巧妙的实现: 需要检查一个对象(Object)是否为空,即不包含任何元素.Javascript 中的对象就是一个字典,其中包含了一系列的键值对(Key Value Pair).检查一个对象是 ...

  9. JavaScript 中的对象

    JavaScript 中的对象 在 JavaScript 中,对象是数据(变量),拥有属性和方法. JavaScript 中的所有事物都是对象:字符串.数字.数组.日期,等等.   访问对象的属性 访 ...

随机推荐

  1. 使用Canal作为mysql的数据同步工具

    一.Canal介绍 1.应用场景 在前面的统计分析功能中,我们采取了服务调用获取统计数据,这样耦合度高,效率相对较低,目前我采取另一种实现方式,通过实时同步数据库表的方式实现,例如我们要统计每天注册与 ...

  2. Python数据模型与Python对象模型

    数据模型==对象模型 Python官方文档说法是"Python数据模型",大多数Python书籍作者说法是"Python对象模型",它们是一个意思,表示&quo ...

  3. 十九、更改LCD显示屏

    一.裸机修改 之前测试用的屏幕是480*272的分辨率,现在要换成800*480的屏,因此要对软件代码进行修改. 对于裸机驱动而言,主要有两个点需要注意,一个是屏幕分辨率变了,因此初始化的时候与屏幕分 ...

  4. 【UltraISO】中文破解版

    下载链接:https://cn.ultraiso.net/uiso9_cn.exe简体中文版专用:   注册名:Guanjiu    注册码:A06C-83A7-701D-6CFC多国语言版专用:   ...

  5. 接口 Interfaces

    Interfaces - zope.interface 5.0.2.dev0 documentation https://zopeinterface.readthedocs.io/en/latest/ ...

  6. Ubuntu16 安装 OpenSSH-Server

    Ubuntu16.04 桌面版默认是没有安装 SSH 服务的,需要手动安装服务: 更新源:sudo apt-get update 安装服务:sudo apt-get install -y openss ...

  7. RPM 和YUM总结

    RPM RPM命名: 安装 rpm -ihv 其他常用的选项: 1. 重新安装 --replacepkgs (或者 --force ) 2. 不考虑依赖 --nodeps (不推荐) 升级: 查询: ...

  8. jsaper子报表Subreport(父子报表互相传值)

    有很多人都说Jasperreports不适合中国式复杂报表,实际上运用好父子报表可以解决大部分问题了.例如下面的表.每个学生的学科数目不固定,且每个学生后有相当于小计的平均分.有点复杂度的报表,可以使 ...

  9. centos 7_本地源制作

    1.安装工具 yum install yum-utils createrepo yum-plugin-priorities   2.自己创建一个阿里源 vim /etc/yum.repos.d/ope ...

  10. K8s 二、(1、kubeadm部署Kubernetes集群)

    准备工作 满足安装 Docker 项目所需的要求,比如 64 位的 Linux 操作系统.3.10 及以上的内核版本: x86 或者 ARM 架构均可: 机器之间网络互通,这是将来容器之间网络互通的前 ...