原来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 相关文章推荐
javascript基础之查找元素的详细介绍(访问节点)
Jul 05 Javascript
Javascript倒计时页面跳转实例小结
Sep 11 Javascript
js中继承的几种用法总结(apply,call,prototype)
Dec 26 Javascript
javascript修改IMG标签的src问题
Mar 28 Javascript
jQery使网页在显示器上居中显示适用于任何分辨率
Jun 09 Javascript
Bootstrap自动适应PC、平板、手机的Bootstrap栅格系统
May 27 Javascript
AngularJS之页面跳转Route实例代码
Mar 10 Javascript
Node.js  REPL (交互式解释器)实例详解
Aug 06 Javascript
vue路由懒加载的实现方法
Mar 12 Javascript
webstorm和.vue中es6语法报错的解决方法
May 08 Javascript
Vue中用props给data赋初始值遇到的问题解决
Nov 27 Javascript
ES6新增的数组知识实例小结
May 23 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异常处理技术,顶级异常处理器
2012/06/13 PHP
Zend Framework 2.0事件管理器(The EventManager)入门教程
2014/08/11 PHP
php实现搜索一维数组元素并删除二维数组对应元素的方法
2015/07/06 PHP
PHP中的Session对象如何使用
2015/09/25 PHP
PHP手机短信验证码实现流程详解
2018/05/17 PHP
什么是PHP7中的孤儿进程与僵尸进程
2019/04/14 PHP
JavaScript入门教程(1) 什么是JS
2009/01/31 Javascript
javascript快速排序算法详解
2014/09/17 Javascript
分享2个jQuery插件--jquery.fileupload与artdialog
2014/12/26 Javascript
js原型链与继承解析(初体验)
2016/05/09 Javascript
input 禁止输入特殊字符的四种实现方式
2016/08/24 Javascript
浅析Jquery操作select
2016/12/13 Javascript
Angular.js去除页面中显示的空行方法示例
2017/03/30 Javascript
浅谈Express异步进化史
2017/09/09 Javascript
JS+canvas动态绘制饼图的方法示例
2017/09/12 Javascript
javascript+jQuery实现360开机时间显示效果
2017/11/03 jQuery
vue路由前进后退动画效果的实现代码
2018/12/10 Javascript
微信小程序如何自定义table组件
2019/06/29 Javascript
使用 webpack 插件自动生成 vue 路由文件的方法
2019/08/20 Javascript
vue 实现购物车总价计算
2019/11/06 Javascript
详解vue中在循环中使用@mouseenter 和 @mouseleave事件闪烁问题解决方法
2020/04/07 Javascript
Layer UI表格列日期格式化及取消自动填充日期的实现方法
2020/05/10 Javascript
vue + el-form 实现的多层循环表单验证
2020/11/25 Vue.js
python关于矩阵重复赋值覆盖问题的解决方法
2019/07/19 Python
解析python实现Lasso回归
2019/09/11 Python
python读取Kafka实例
2019/12/23 Python
Python连接HDFS实现文件上传下载及Pandas转换文本文件到CSV操作
2020/06/06 Python
Python自动化xpath实现自动抢票抢货
2020/09/19 Python
Python中return函数返回值实例用法
2020/11/19 Python
css3简单练习实现遨游浏览器logo的绘制
2013/01/30 HTML / CSS
JD Sports芬兰:英国领先的运动鞋和运动服饰零售商
2018/11/16 全球购物
最常使用的求职信
2014/05/25 职场文书
工地质量标语
2014/06/12 职场文书
清洁工个人总结
2015/03/04 职场文书
2019年个人工作总结范文(3篇)
2019/08/27 职场文书
python在package下继续嵌套一个package
2022/04/14 Python