浅析JavaScript对象转换成原始值
发布时间:2023-08-05 02:09:11
前言
首先抛出几个问题:
console.log(Boolean({}));
console.log(Number([]));
console.log(Number([6]));
console.log(([] + []).length);
console.log(({} + {}).length);
打印结果都是啥?不知道各位能答对几题。文章末尾,我们会揭晓答案。这篇文章建议大家耐心阅读,相对来说比较绕而且乏味 - -。
三种算法
JavaScript 将对象转换为原始值时遵循的算法规则比较复杂,这些规则冗长、晦涩,我们先简单了解一下,具体实现后面再详细展开。
对象转换成原始值有三种基本算法:
偏字符串算法。该算法返回原始值,而且只要可能就返回字符串。
偏数值算法。该算法返回原始值,而且只要可能就返回数值。
无偏好算法。该算法不倾向于任何原始值类型,而是由类定义自己的转换规则。JavaScript 内置类除了 Date 类,其他都实现了偏数值算法。Date 类实现了偏字符串算法。
对象转换成布尔值
对象到布尔值的转换最简单:所有对象都转换为true
。这个转换不需要使用前面介绍的转换算法,直接适用于所有对象,包括空数组、包装对象:
console.log(Boolean([])); // => true
const wrapperObject = new Boolean(false); // 这是一个对象,而不是原始值
console.log(Boolean(wrapperObject)); // => true
对象转换成字符串
在将对象转换成字符串时,首先使用偏字符串算法将它转换为一个原始值,然后将得到的原始值再转换为字符串。
这种转换会发生在把对象传给一个接收字符串参数的内置函数时,比如将String()
作为转换函数,或者将对象插入模板字面量中时就会发生这种转换:
console.log(String({})); // => '[object Object]'
console.log(String([])); // => ''
console.log(String(function () {})); // => 'function () {}'
对象转换成数值
当需要把对象转换为数值时,首先使用偏数值算法将它转换为一个原始值,然后将得到的原始值再转换为数值。
接收数值参数的内置函数和方法都以这种方式将对象转换为数值,除数值操作符之外的多数操作符也按照这种方式把对象转换为数值:
console.log(Number([])); // => 0
console.log(Number({})); // => NaN
console.log(+[]); // => 0
上面的栗子了解一下即可,具体的转换算法后面会详细解释。
转换时使用的方法
toString()
、valueOf()
,所有对象都会继承这两个在对象到原始值转换时使用的方法,在接下来解释偏字符串、偏数值、无偏好转换算法之前,我们必须先了解这两个方法。
toString()
toString()
的任务是返回对象的字符串表示。
默认情况下,toString()
方法会返回特殊值:
const obj = {
x: 1,
y: 2,
}
obj.toString(); // => '[object Object]'
但是很多类都定义了自己特有的toString()
版本。
比如,Array
类的toString()
方法会将数组的每个元素转换为字符串,然后再使用逗号作为分隔符将它们拼接起来:
const array = [1, 2, 3];
array.toString(); // => '1', '2', '3'
Function
类的toString()
方法会将用户定义的函数转换为 JavaScript 源代码的字符串:
const f = function () {};
f.toString() // => 'function () {}'
Date
类定义的toString()
方法返回一个对人类友好的日期和时间字符串。
RegExp
类定义的toString()
方法会将RegExp
对象转换为一个看起来像RegExp
字面量的字符串。
valueOf()
valueOf()
方法的设计意图是返回对象的原始值表示。
然而大多数对象都没有缺省的原始值表示,为此valueOf()
的默认实现是返回对象本身,内置的函数、数组等类型都是如此:
const obj = {x: 666};
const arr = [1, 2, 3];
const fn = function () {};
obj.valueOf(); // => {x: 666}
arr.valueOf(); // => [1, 2, 3]
fn.valueOf(); // => ƒ () {}
极少数对象才具有有意义的原始值表示,比如String
、Number
、Boolean
这样的包装类定义的valueOf()
方法会简单地返回被包装的原始值:
const wrapperObj1 = new String(666);
const wrapperObj2 = new Number(888);
const wrapperObj3 = new Boolean(false);
wrapperObj1.toString(); // => '666'
wrapperObj2.toString(); // => '888'
wrapperObj3.toString(); // => 'false'
还有Date
对象定义的valueOf()
方法返回时间戳:
const d = new Date(2022, 6, 24);
d.valueOf(); // => 1658592000000
了解完toString()
和valueOf()
方法后,接下来我们看看转换算法是如何实现的。
转换算法
偏字符串算法
偏字符串算法首先尝试toString()
方法。如果这个方法有定义且返回原始值,则使用这个原始值,即使这个值不是字符串。如果toString()
方法不存在,或者存在但返回对象,则尝试valueOf()
方法。如果valueOf()
方法存在且返回原始值,就使用该值。否则,转换失败,报 TypeError。
偏数值算法
偏数值算法与偏字符串算法类似,只不过是先尝试valueOf()
方法,再尝试toString()
方法。
无偏好算法
无偏好算法取决于被转换的对象。如果是一个Date
对象,则使用偏字符串算法。如果是其他类型的对象,就使用偏数值算法。
以上规则适用于所有的内置 JavaScript 类型。
练习题
文章开头列举的五个问题,下面我们逐一揭晓答案。
console.log(Boolean({}))
:对象转换成布尔值时,所有对象都转换为true
,所以打印true
。
console.log(Number([]))
:对象转换成数值,首先使用偏数值算法把对象转换为一个原始值,然后再把得到的原始值转换为数值。偏数值算法会先尝试valueOf()
,将toString()
作为备用。Array
类继承了默认的valueOf()
方法,所以不会返回原始值,因此最终会调用toString()
方法。空数组会被转换成空字符串,而空字符串转换成数值为 0,所以打印 0。
console.log(Number([6]))
:同样,使用偏数值算法,最终会调用toString()
方法。因为数组中只包含一个数值,所以该数值首先会被转换成字符串 '6',再转换回数值 6,打印结果为 6。
console.log(([] + []).length)
:两个数组相加,首先会将数组转换为字符串,使用偏字符串算法,空数组会转换为空字符串,空字符串相加还是空字符串,长度为 0,打印结果为 0。
console.log(({} + {}).length)
:两个对象相加,首先会将对象转换成字符串,使用偏字符串算法,对象转换成字符串后是'[object Object]'
,两个'[object Object]'
拼接后的长度是 30,打印结果为 30。
来源:https://juejin.cn/post/7123780785097670686


猜你喜欢
- 什么是PRC&GRPCRPC是远程过程调用(Remote Procedure Call)的缩写形式, RPC 的主要功能目标是让构建
- Matrix是Array的一个小的分支,包含于Array。所以matrix 拥有array的所有特性。但在数组乘和矩阵乘时,两者各有不同,如
- 本文实例讲述了PHP读取txt文本文件并分页显示的方法。分享给大家供大家参考。具体实现方法如下:<?php &n
- 1)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。2)应尽量避免在 where 子
- 刚刚解决了这个问题,现在记录下来问题描述当使用lambda层加入自定义的函数后,训练没有bug,载入保存模型则显示Nonetype has
- 对win32 COM不是很熟悉,不知道一个程序究竟有多少属性或者方法可以操作。仅仅是一个Sheet页的添加就费了我好长时间,因为这种成功来自
- 前面介绍了关于用户账户的User表,但是现实生活中随着问题的复杂化数据库存储的数据不可能这么简单,让我们设想有另外一张表,这张表和User有
- 调用百度API获取经纬度信息。import requestsimport jsonaddress = input('请输入地点:
- 数据库系统的安全性包括很多方面。由于很多情况下,数据库服务器容许客户机从网络上连接,因此客户机连接的安全对MySQL数据库安全有很重要的影响
- 今天比较忙,水一下下面的代码来源于这个视频里面提到的,github 的链接为:github.com/mikeckenned…(本地下载)第一
- 本文为大家分享了opencv图片模糊和锐化的具体实现代码,供大家参考,具体内容如下一、模糊操作#!/usr/bin/env python#
- 1. 背景:最近写了一篇CSDN博客需要上传gif图,发现大小超过了5M,无法上传。文件大小:本想自己找个免费的压缩工具,结果下载下来的工具
- 过滤器是一个通过输入数据,能够及时对数据进行处理并返回一个数据结果的简单函数。Vue有很多很便利的过滤器,过滤器通常会使用管道标志 “ |
- 前言Golang 是一种并发友好的语言,使用 goroutines 和 channels 可以轻松地实现多线程爬虫。具体地说,实现的是多协程
- //实例化上传类$upload = new Zend_File_Transfer();//设置过滤器,大小限制为5M,格式为jpg,gif,
- 本文实例讲述了python中urllib模块用法。分享给大家供大家参考。具体分析如下:一、问题:近期公司项目的需求是根据客户提供的api,我
- 图片的宽度和高度是未知的,没有一个固定的尺寸,在这个前提下要使图片在一个固定了宽度和高度的容器中垂直居中,想想感觉还是挺麻烦的,由于最近的项
- 序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。Pyt
- 本文实例讲述了Python使用百度翻译开发平台实现英文翻译为中文功能。分享给大家供大家参考,具体如下:#coding=utf8import
- 环境:numpy,pandas,python3在机器学习和深度学习的过程中,对于处理预测,回归问题,有时候变量是时间,需要进行合适的转换处理