原来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 相关文章推荐
自动完成JS类(纯JS, Ajax模式)
Mar 12 Javascript
js表格分页实现代码
Sep 18 Javascript
JavaScrip单线程引擎工作原理分析
Sep 04 Javascript
window.open的页面如何刷新(父页面)上层页面
Dec 28 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
Nov 14 Javascript
JavaScript 浏览器对象模型BOM使用介绍
Apr 13 Javascript
Jquery实现弹性滑块滑动选择数值插件
Aug 08 Javascript
JavaScript中的工厂函数(推荐)
Mar 08 Javascript
Jquery EasyUI $.Parser
Jun 02 jQuery
详解Vue 动态组件与全局事件绑定总结
Nov 11 Javascript
javascript中的with语句学习笔记及用法
Feb 17 Javascript
基于JavaScript实现大文件上传后端代码实例
Aug 18 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/01/22 PHP
CSS+Table图文混排中实现文本自适应图片宽度(超简单+跨所有浏览器)
2009/02/14 Javascript
jquery 表单取值常用代码
2009/12/22 Javascript
捕获键盘事件(且兼容各浏览器)
2013/07/03 Javascript
js模拟hashtable的简单实例
2014/03/06 Javascript
点击A元素触发B元素的事件在IE8下会识别成A元素
2014/09/04 Javascript
node.js中的fs.lchmod方法使用说明
2014/12/16 Javascript
jQuery extend()详解及简单实例
2017/05/06 jQuery
结合Vue控制字符和字节的显示个数的示例
2018/05/17 Javascript
vue使用jsonp抓取qq音乐数据的方法
2018/06/21 Javascript
JS实现电话号码的字母组合算法示例
2019/02/26 Javascript
js比较两个单独的数组或对象是否相等的实例代码
2019/04/28 Javascript
js如何实现元素曝光上报
2019/08/07 Javascript
AntV F2和vue-cli构建移动端可视化视图过程详解
2019/10/08 Javascript
JQuery复选框全选效果如何实现
2020/05/08 jQuery
vue 获取元素额外生成的data-v-xxx操作
2020/09/09 Javascript
Python中利用Scipy包的SIFT方法进行图片识别的实例教程
2016/06/03 Python
浅谈python字符串方法的简单使用
2016/07/18 Python
python实现桌面壁纸切换功能
2019/01/21 Python
python 发送和接收ActiveMQ消息的实例
2019/01/30 Python
Tensorflow之MNIST CNN实现并保存、加载模型
2020/06/17 Python
PyCharm 2020.1版安装破解注册码永久激活(激活到2089年)
2020/09/24 Python
Pandas中DataFrame交换列顺序的方法实现
2020/12/14 Python
斐乐美国官方网站:FILA美国
2019/03/01 全球购物
数据库设计的包括哪两种,请分别进行说明
2016/07/15 面试题
简单叙述一下MYSQL的优化
2016/05/09 面试题
方正Java笔试题
2014/07/03 面试题
房地产资料员岗位职责
2014/07/02 职场文书
感恩教师节演讲稿
2014/09/03 职场文书
档案接收函格式
2015/01/30 职场文书
团员年度个人总结
2015/02/26 职场文书
中秋晚会致辞
2015/07/31 职场文书
某某店铺的开业庆典主持词范本
2019/11/25 职场文书
用Python提取PDF表格的方法
2021/04/11 Python
一次线上mongo慢查询问题排查处理记录
2022/03/18 MongoDB
MySQL库表太大怎么办? 数据库分库分表项目实践
2022/04/11 MySQL