原来JS还可以这样拆箱转换详解


Posted in Javascript onFebruary 01, 2019

前言

在读 Winter 大佬的《重学前端》栏目时,重温了 JS 的「拆箱转换」。「装箱转换」与「拆箱转换」以前都是了解的,今天来看,自己所谓的了解也真是一知半解。在阅读 Winter 老师写的内容后,对「拆箱转换」这个知识点还是不甚清楚,因此我再去深入地了解一番,参考资料详见文末的「参考链接」。

被我们忽略的表象

首先,我们来看一下例子:

const a = {
 name: 'a',
 toString () {
  console.log(this);
  console.log('toString');
  return { name: 'toString' };
 },
 valueOf () {
  console.log(this);
  console.log('valueOf');
  return { name: 'valueOf' };
 }
};

a * 2;
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive value

a + "";
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive 

alert(a);
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value

可以看到,toString 和 valueOf 的执行顺序并不固定,而是根据某个条件来决定的,那么是根据什么呢?那就是在拆箱转换时,调用了对象的 ToPrimitive 内部函数时,其会根据执行上下文,自动传入一个转换类型参数,暂时给它命名为 hint。

ToPrimitive

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型转换的实现者(即,拆箱转换);但这是一个内部算法,是编程语言在内部执行时遵循的一套规则。

对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。

但是对于不同的操作,拆箱转换的内部实现也有所区别,正如上面的例子所示。

「拆箱转换」的调用规则及顺序如下:

  • 检查对象中是否有用户显式定义的 [Symbol.toPrimitive] 方法,如果有,直接调用;
  • 如果没有,则执行原内部函数 ToPrimitive,然后判断传入的 hint 值,如果其值为 string,顺序调用对象的 toString 和 valueOf 方法(其中 toString 方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用 valueOf 方法);
  • 如果判断传入的 hint 值不为 string,则就可能为 number 或者 default 了,均会顺序调用对象的 valueOf 和 toString 方法(其中 valueOf 方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用 toString 方法);

来看一下第一种情况:

const b = {
 [Symbol.toPrimitive] (hint) {
  console.log(`hint: ${hint}`);
  return {};
 },
 toString () {
  console.log('toString');
  return 1;
 },
 valueOf () {
  console.log('valueOf');
  return 2;
 }
};

alert(b); // hint: string 
b + ''; // hint: default
b + 500; // hint: default
+b; // hint: number
b * 1; // hint: number

第二、三种情况:

const c = {
 toString () {
  console.log('toString');
  return 1;
 },
 valueOf () {
  console.log('valueOf');
  return 2;
 }
};

alert(c); // 打印出 toString 并 alert 出 1
c + ''; // 先后打印出 valueOf,"2"
c + 500; // 先后打印出 valueOf,502
+c; // 先后打印出 valueOf,2
c * 1; // 先后打印出 valueOf,2

那么关于 hint 可取的三种值,都有什么含义?又什么情况对应什么值?

确定 hint 取值

string

当在希望是字符串操作,也即发生对象到字符串的转换时,传入内部函数 ToPrimitive 的参数值即为 string:

// output
alert(obj);

// using object as a property key
anotherObj[obj] = 123;

number

当在希望是数值操作,也即发生对象到数值的转换时,传入内部函数 ToPrimitive 的参数值即为 number:

// explicit conversion
let num = Number(obj);

// maths (except binary plus)
let n = +obj; // unary plus
let delta = date1 - date2;

// less/greater comparison
let greater = user1 > user2;

default

当在一些不确定需要将对象转换成什么基础类型的场景下,传入内部函数 ToPrimitive 的参数值即为 default:

// binary plus
let total = car1 + car2;

// obj == string/number/symbol
if (user == 1) { ... };

结语

如果亲爱的读者们在本文中发现了什么错误,或者有什么不同的意见,还请留言,一起讨论,一起将隐藏的、晦涩的点提出来,然后解决掉。

参考链接

  • 《重学前端》——极客时间APP
  • Object to primitive conversion
  • ToPrimitive

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JSON 编辑器实现代码
Dec 06 Javascript
查询绑定数据岛的表格中的文本并修改显示方式的js代码
Dec 15 Javascript
Kibo 用于处理键盘事件的Javascript工具库
Oct 28 Javascript
js 实现css风格选择器(压缩后2KB)
Jan 12 Javascript
jquery用get实现ajax在ie里面刷新不进入后台解决方法
Aug 12 Javascript
22点关于jquery性能优化的建议
May 28 Javascript
JavaScript中property和attribute的区别详细介绍
Mar 03 Javascript
轻松学习jQuery插件EasyUI EasyUI实现树形网络基本操作(2)
Nov 30 Javascript
JavaScript程序中实现继承特性的方式总结
Jun 24 Javascript
JS时间控制实现动态效果的实例讲解
Jul 31 Javascript
详解html-webpack-plugin用法全解
Jan 22 Javascript
swiper4实现移动端导航切换
Oct 16 Javascript
微信小程序拍照和摄像功能实现方法示例
Feb 01 #Javascript
微信小程序常用简易小函数总结
Feb 01 #Javascript
使用Angular自定义字段校验指令的方法示例
Feb 01 #Javascript
angular中如何绑定iframe中src的方法
Feb 01 #Javascript
Javascript迭代、递推、穷举、递归常用算法实例讲解
Feb 01 #Javascript
JS集合set类的实现与使用方法示例
Feb 01 #Javascript
详解如何使用webpack打包多页jquery项目
Feb 01 #jQuery
You might like
PHP 采集程序原理分析篇
2010/03/05 PHP
学习php笔记 字符串处理
2010/10/19 PHP
md5 16位二进制与32位字符串相互转换示例
2013/12/30 PHP
JS支持带x身份证号码验证函数
2008/08/10 Javascript
点击文章内容处弹出页面代码
2009/10/01 Javascript
Jquery Autocomplete 结合asp.net使用要点
2010/10/29 Javascript
关于javaScript注册click事件传递参数的不成功问题
2014/07/18 Javascript
jquery控制表单输入框显示默认值的方法
2015/05/22 Javascript
深入理解jQuery()方法的构建原理
2016/12/05 Javascript
js模拟微博发布消息
2017/02/23 Javascript
canvas压缩图片转换成base64格式输出文件流
2017/03/09 Javascript
BootStrap表单宽度设置方法
2017/03/10 Javascript
easyui datagrid 表格中操作栏 按钮图标不显示的解决方法
2017/07/27 Javascript
浅谈angular4生命周期钩子
2017/09/05 Javascript
vue 微信授权登录解决方案
2018/04/10 Javascript
Nodejs 发布自己的npm包并制作成命令行工具的实例讲解
2018/05/15 NodeJs
详解微信小程序中组件通讯
2018/10/30 Javascript
微信小程序聊天功能的示例代码
2020/01/13 Javascript
实例讲解JavaScript 计时事件
2020/07/04 Javascript
在Django的上下文中设置变量的方法
2015/07/20 Python
python subprocess 杀掉全部派生的子进程方法
2017/01/16 Python
Python中Numpy mat的使用详解
2019/05/24 Python
Python爬虫图片懒加载技术 selenium和PhantomJS解析
2019/09/18 Python
pygame实现非图片按钮效果
2019/10/29 Python
Python 内存管理机制全面分析
2021/01/16 Python
Stuart Weitzman美国官网:美国奢华鞋履品牌
2016/08/18 全球购物
英国最专业的健身器材供应商之一:Best Gym Equipment
2017/12/22 全球购物
医院护士专业个人的求职信
2013/12/09 职场文书
3分钟英语演讲稿
2014/04/29 职场文书
技术经济专业求职信
2014/09/03 职场文书
学习型家庭事迹材料
2014/12/20 职场文书
绍兴鲁迅故居导游词
2015/02/09 职场文书
小英雄雨来观后感
2015/06/09 职场文书
Python爬虫进阶之Beautiful Soup库详解
2021/04/29 Python
Python下opencv使用hough变换检测直线与圆
2021/06/18 Python
SQL Server数据库基本概念、组成、常用对象与约束
2022/03/20 SQL Server