原来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调试说明
Jun 07 Javascript
js换图片效果可进行定时操作
Jun 09 Javascript
技术男用来对妹子表白的百度首页
Jul 23 Javascript
JavaScript实现的微信二维码图片生成器的示例
Oct 26 Javascript
微信小程序 picker-view 组件详解及简单实例
Jan 10 Javascript
用最少的JS代码写出贪吃蛇游戏
Jan 12 Javascript
Hexo已经看腻了,来手把手教你使用VuePress搭建个人博客
Apr 26 Javascript
angularJs中$http获取后台数据的实例讲解
Aug 08 Javascript
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
Mar 06 Javascript
JavaScript遍历数组和对象的元素简单操作示例
Jul 09 Javascript
解决layer.open后laydate失效的问题
Sep 06 Javascript
vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据操作
Jul 31 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
阿拉伯的咖啡与水烟
2021/03/03 咖啡文化
PHP网页游戏学习之Xnova(ogame)源码解读(二)
2014/06/23 PHP
windows server 2008/2012安装php iis7 mysql环境搭建教程
2016/06/30 PHP
Javascript优化技巧(文件瘦身篇)
2008/01/28 Javascript
JS写的贪吃蛇游戏(个人练习)
2013/07/08 Javascript
JS图片自动轮换效果实现思路附截图
2014/04/30 Javascript
JavaScript中Number.NEGATIVE_INFINITY值的使用详解
2015/06/05 Javascript
详解AngularJS中自定义过滤器
2015/12/28 Javascript
JS集成fckeditor及判断内容是否为空的方法
2016/05/27 Javascript
jQuery获取多种input值的简单实现方法
2016/06/20 Javascript
jQuery on()方法绑定动态元素的点击事件无响应的解决办法
2016/07/07 Javascript
JavaScript页面实时显示当前时间实例代码
2016/10/23 Javascript
js原生之焦点图转换加定时器实例
2016/12/12 Javascript
react-native组件中NavigatorIOS和ListView结合使用的方法
2017/09/30 Javascript
详解使用PM2管理nodejs进程
2017/10/24 NodeJs
在 vue-cli v3.0 中使用 SCSS/SASS的方法
2018/06/14 Javascript
微信小程序登录session的使用
2019/03/17 Javascript
element表格翻页第2页从1开始编号(后端从0开始分页)
2019/12/10 Javascript
Vue组件模板的几种书写形式(3种)
2020/02/19 Javascript
Python+OpenCV实现图像融合的原理及代码
2018/12/03 Python
python+opencv实现阈值分割
2018/12/26 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
2019/01/29 Python
python实现在函数图像上添加文字和标注的方法
2019/07/08 Python
新手入门Python编程的8个实用建议
2019/07/12 Python
对Python中一维向量和一维向量转置相乘的方法详解
2019/08/26 Python
PyCharm无法识别PyQt5的2种解决方法,ModuleNotFoundError: No module named 'pyqt5'
2020/02/17 Python
CSS3 Media Queries详细介绍和使用实例
2014/05/08 HTML / CSS
abstract是什么意思
2012/02/12 面试题
大三自我鉴定范文
2013/10/05 职场文书
餐厅经理岗位职责和岗位目标
2014/02/13 职场文书
新闻专业学生的自我评价
2014/02/13 职场文书
《金钱的魔力》教学反思
2014/02/24 职场文书
骨干教师考核方案
2014/05/09 职场文书
结婚保证书(卖身契)
2015/02/26 职场文书
JS不要再到处使用绝对等于运算符了
2021/04/30 Javascript
试了下Golang实现try catch的方法
2021/07/01 Golang