原来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 静态页面实现随机显示广告的办法
Nov 17 Javascript
js有序数组的连接问题
Oct 01 Javascript
js实现单行文本向上滚动效果实例代码
Nov 28 Javascript
浅谈javascript中自定义模版
Jan 29 Javascript
jQuery检查事件是否触发的方法
Jun 26 Javascript
谈一谈js中的执行环境及作用域
Mar 30 Javascript
JS 全屏和退出全屏详解及实例代码
Nov 07 Javascript
Vue触发式全局组件构建的方法
Nov 28 Javascript
详解滑动穿透(锁body)终极探索
Apr 16 Javascript
JavaScript遍历数组和对象的元素简单操作示例
Jul 09 Javascript
vuex(vue状态管理)的特殊应用案例分享
Mar 03 Javascript
react-router-dom 嵌套路由的实现
May 02 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
一个连接两个不同MYSQL数据库的PHP程序
2006/10/09 PHP
php强制下载类型的实现代码
2011/04/21 PHP
PHP函数之日期时间函数date()使用详解
2013/09/09 PHP
PHP使用PDO操作数据库的乱码问题解决方法
2016/04/08 PHP
thinkphp3.2实现上传图片的控制器方法
2016/04/28 PHP
PHP获取用户访问IP地址的5种方法
2016/05/16 PHP
解决thinkphp5未定义变量会抛出异常,页面错误,请稍后再试的问题
2019/10/16 PHP
有关js的变量作用域和this指针的讨论
2010/12/16 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
2012/08/17 Javascript
JavaScript NodeTree导航栏(菜单项JSON类型/自制)
2013/02/01 Javascript
使用非html5实现js板连连看游戏示例代码
2013/09/22 Javascript
判断输入是否为空,获得输入类型的JS代码
2013/10/30 Javascript
javascript中一些util方法汇总
2015/06/10 Javascript
用javascript实现自动输出网页文本
2015/07/30 Javascript
Javascript字符串常用方法详解
2016/07/21 Javascript
JavaScript中removeChild 方法开发示例代码
2016/08/15 Javascript
Angular.js中$apply()和$digest()的深入理解
2016/10/13 Javascript
老生常谈js中0到底是 true 还是 false
2017/03/08 Javascript
Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解
2017/08/01 jQuery
用Vue写一个分页器的示例代码
2018/04/22 Javascript
Python正则表达式教程之三:贪婪/非贪婪特性
2017/03/02 Python
详解Python中最难理解的点-装饰器
2017/04/03 Python
Python使用tkinter库实现文本显示用户输入功能示例
2018/05/30 Python
python 读取文件并替换字段的实例
2018/07/12 Python
python实现对象列表根据某个属性排序的方法详解
2019/06/11 Python
python通过SSH登陆linux并操作的实现
2019/10/10 Python
Python数据可视化处理库PyEcharts柱状图,饼图,线性图,词云图常用实例详解
2020/02/10 Python
Python切割图片成九宫格的示例代码
2020/03/10 Python
解决Keras自带数据集与预训练model下载太慢问题
2020/06/12 Python
python中requests模拟登录的三种方式(携带cookie/session进行请求网站)
2020/11/17 Python
Python基础进阶之海量表情包多线程爬虫功能的实现
2020/12/17 Python
印度尼西亚综合购物网站:Lazada印尼
2016/09/07 全球购物
美国狗旅行和户外用品领先供应商:kurgo
2020/08/18 全球购物
会计岗位职责范本
2015/04/02 职场文书
小学体育组工作总结
2015/08/13 职场文书
一篇文章了解正则表达式的替换技巧
2022/02/24 Javascript