原来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常用方法、属性集合及NodeList 和 HTMLCollection 的浏览器差异
Dec 25 Javascript
poshytip 基于jquery的 插件 主要用于显示微博人的图像和鼠标提示等
Oct 12 Javascript
用示例说明filter()与find()的用法以及children()与find()的区别分析
Apr 26 Javascript
JSONP获取Twitter和Facebook文章数的具体步骤
Feb 24 Javascript
javascript中数组方法汇总
Jul 07 Javascript
javaScript给元素添加多个class的简单实现
Jul 20 Javascript
微信小程序教程系列之新建页面(4)
Apr 17 Javascript
手动用webpack搭建第一个ReactApp的示例
Apr 11 Javascript
vue+moment实现倒计时效果
Aug 26 Javascript
JS原型prototype和__proto__用法实例分析
Mar 14 Javascript
详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()
May 12 Javascript
浅谈JavaScript 声明提升
Sep 14 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模拟post行为代码总结(POST方式不是绝对安全)
2012/02/22 PHP
php+xml结合Ajax实现点赞功能完整实例
2015/01/30 PHP
js split 的用法和定义 js split分割字符串成数组的实例代码
2012/05/13 Javascript
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
2012/08/14 Javascript
Raphael带文本标签可拖动的图形实现代码
2013/02/20 Javascript
js鼠标及对象坐标控制属性详细解析
2013/12/14 Javascript
jQuery使用之标记元素属性用法实例
2015/01/19 Javascript
javascript实现密码验证
2015/11/10 Javascript
vue表单绑定实现多选框和下拉列表的实例
2017/08/12 Javascript
微信小程序页面生命周期详解
2018/01/31 Javascript
如何从零开始利用js手写一个Promise库详解
2018/04/19 Javascript
使用vue 国际化i18n 实现多实现语言切换功能
2018/10/11 Javascript
详解解决Vue相同路由参数不同不会刷新的问题
2018/10/12 Javascript
详解在vue-cli3.0中自定css、js和图片的打包路径
2019/08/26 Javascript
vue路由 遍历生成复数router-link的例子
2019/10/30 Javascript
vue 自定义右键样式的实例代码
2019/11/06 Javascript
vue实现五子棋游戏
2020/05/28 Javascript
vue抽出组件并传值实例
2020/07/31 Javascript
Python中Collection的使用小技巧
2014/08/18 Python
Python实现数据库编程方法详解
2015/06/09 Python
Python实现随机取一个矩阵数组的某几行
2019/11/26 Python
基于Tensorflow读取MNIST数据集时网络超时的解决方式
2020/06/22 Python
【HTML5】3D模型--百行代码实现旋转立体魔方实例
2016/12/16 HTML / CSS
阿根廷网上配眼镜:SmartBuyGlasses阿根廷
2016/08/19 全球购物
英国在线珠宝店:The Jewel Hut
2017/03/20 全球购物
REN Clean Skincare官网:英国本土有机护肤品牌
2019/02/23 全球购物
后勤主管工作职责
2013/12/07 职场文书
会计专业求职信范文
2014/03/16 职场文书
三方合作协议书范本
2014/04/18 职场文书
学生检讨书如何写
2014/10/30 职场文书
转让协议书
2015/01/27 职场文书
2015年物流客服工作总结
2015/07/27 职场文书
母亲节主题班会
2015/08/14 职场文书
idea编译器vue缩进报错问题场景分析
2021/07/04 Vue.js
python 远程执行命令的详细代码
2022/02/15 Python
Java 超详细讲解ThreadLocal类的使用
2022/04/07 Java/Android