原来JS还可以这样拆箱转换详解

前言

在读 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) { ... };

结语

如果亲爱的读者们在本文中发现了什么错误,或者有什么不同的意见,还请留言,一起讨论,一起将隐藏的、晦涩的点提出来,然后解决掉。

参考链接

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • js 将json字符串转换为json对象的方法解析

    例如: JSON字符串: var str1 = '{ "name": "cxh", "sex": "man" }'; JSON对象: var str2 = { "name": "cxh", "sex": "man" }; 一.JSON字符串转换为JSON对象 要使用上面的str1,必须使用下面的方法先转化为JSON对象: //由JSON字符串转换为

  • js数组与字符串的相互转换方法

    熟悉js的朋友很多都遇到过js的数组与字符串相互转换的情况,本文就此作一简单介绍,示例如下: 一.数组转字符串 需要将数组元素用某个字符连接成字符串,示例代码如下: var a, b; a = new Array(0,1,2,3,4); b = a.join("-"); 二.字符串转数组 实现方法为将字符串按某个字符切割成若干个字符串,并以数组形式返回,示例代码如下: var s = "abc,abcd,aaa"; ss = s.split(","

  • js中将字符串转换成json的三种方式

    ECMA-262(E3) 中没有将JSON概念写到标准中,还好在 ECMA-262(E5)中JSON的概念被正式引入了,包括全局的JSON对象和Date的toJSON方法. 1,eval方式解析,恐怕这是最早的解析方式了.如下: 复制代码 代码如下: function strToJson(str){ var json = eval('(' + str + ')'); return json; } 记得别忘了str两旁的小括号. 2,new Function形式,比较怪异哦.如下 复制代码 代码如

  • JS对象与JSON格式数据相互转换

    目前的项目数据交互几乎都用JQuery,所以处理流程是:前端页面数据->JS对象->jQuery提交->python处理,另外一种就是倒过来.python肯定不能直接处理JS对象数据,所以要把JS对象转换成为python能处理的一种数据格式(通常是字典dict),同样,python取数据反馈到前端也要把字典数据转换成JS能处理的对象,这个中间转换数据格式通常就是JSON. 一.JS对象转换成为JSON 流程:读取前端页面数据,组装成为JS对象,并通过jQuery的$.post()方法传递

  • js将类数组对象转换成数组对象

    在标准浏览器中,好像只要对象存在length属性,就能把它转换为数组,但IE就不尽然. //※※※※※※※※※※※测试1※※※※※※※※※※※※※※ function test(){ alert(arguments) arguments = Array.prototype.slice.call(arguments); alert(arguments instanceof Array); alert(arguments) } test(1,2,3,4); //※※※※※※※※※※※测试2※※※※※

  • js实现数组转换成json

    js实现数组转换成json function arrayToJson(o) { var r = []; if (typeof o == "string") return "\"" + o.replace(/([\'\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "

  • js字符串转换成数字与数字转换成字符串的实现方法

    js字符串转换成数字 将字符串转换成数字,得用到parseInt函数.parseInt(string) : 函数从string的开始解析,返回一个整数. 举例:parseInt('123') : 返回 123(int):parseInt('1234xxx') : 返回 1234(int): 如果解析不到数字,则将返回一个NaN的值,可以用isNaN()函数来检测: 举例 :var i = parseInt('abc');if (isNaN(i)){alert('NaN value');} 同样的

  • js 字符串转换成数字的三种方法

    方法主要有三种 转换函数.强制类型转换.利用js变量弱类型转换. 1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把值转换成浮点数.只有对String类型调用这些方法,这两个函数才能正确运行:对其他类型返回的都是NaN(Not a Number). 一些示例如下: 复制代码 代码如下: parseInt("1234blue");   //returns   1234parseInt("0xA");  

  • Json对象与Json字符串互转(4种转换方式)

    1>jQuery插件支持的转换方式: 复制代码 代码如下: $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象 2>浏览器支持的转换方式(Firefox,chrome,opera,safari,ie9,ie8)等浏览器: 复制代码 代码如下: JSON.parse(jsonstr); //可以将json字符串转换成json对象 JSON.stringify(jsonobj); //可以将json对象转换

  • 解析JSON对象与字符串之间的相互转换

    在开发的过程中,如果对于少量参数的前后台传递,可以直接采用ajax的data函数,按json格式传递,后台Request即可,但有的时候,需要传递多个参数,这样后台 接受的时候Request多个很麻烦,此时要按照类的格式或者 集合的形式进行传递. 例如:前台按类的格式传递JSON对象: var jsonUserInfo = "{\"TUserName\":\"" + userName + "\",\"TInterest\&qu

随机推荐