细读 JS | valueOf 和 toString 方法

简介: 在讲述ToPrimitive 和 OrdinaryToPrimitive 操作时,涉及到这两方法,所以今天来简单写一下。

前言


在讲述ToPrimitive 和 OrdinaryToPrimitive 操作时,涉及到这两方法,所以今天来简单写一下。

其实我们一般很少主动去调用这两个方法,那它们什么时候会被使用到呢?当我们需要将一个对象(严格来说是,引用类型的值)转化为原始值的时候,JavaScript 可能会调用到它们。


正文


举个例子:

const obj = {}
console.log('This is ' + obj) // "This is [object Object]"


内部过程是这样的:

1. 'This is ' + obj 操作,使得 obj 会自动转换为原始值。
2. 由于 obj 内部没有定义 @@toPrimitive 属性,所以它会先调用 toString 方法或 valueOf 方法。
3. 由于 obj 本身没有 toString 方法,JavaScript 会从原型上找到 Object.prototype.toString(),执行结果是 [object Object]。
4. 由于 toString 方法已经返回原始值了,就不会再调用 valueOf 方法了。(假设上面 toString 没有返回原始值,接着调用 valueOf 方法,如果结果还不是原始值,则会抛出 TypeError 错误)
5. 所以最后执行拼接操作的是两个字符串:'This is ' + '[object Object]',所以结果就是它了。


接着我们来验证一下:

// 示例 1:验证第 2 点。在类型转换时,优先寻找 @@toPrimitive 方法(即下面的 [Symbol.toPrimitive])
const obj = {
  [Symbol.toPrimitive]: hint => {
    console.log('注意:根据 ECMAScript 标准,若该方法返回引用类型,会抛出 TypeError')
    return 'abc'
  }
}
console.log('This is ' + obj) // "This is abc"
// 示例 2:验证第 3 点
const obj = {}
Object.prototype.toString = () => 'rewrite toString method'
console.log('This is ' + obj) // "This is rewrite toString method"
// 示例 3:验证第 4 点
const obj = {
  valueOf: hint => {
    console.log('执行 valueOf 方法')
    return {}
  },
  toString: hint => {
    console.log('执行 toString 方法')
    return 'tostring'
  }
}
console.log('This is ' + obj) // "This is tostring"(在控制台可以看到,先后执行了 valueOf、toString 方法)
console.log(String(obj)) // "tostring"


一、valueOf


OrdinaryToPrimitive(O, hint) 抽象操作的 hint"number" 时,JavaScript 会首先调用 valueOf() 方法。


1. Object.prototype.valueOf


Object.prototype.valueOf() 方法返回对象的原始值

Object.prototype.valueOf.call({}) // {}
Object.prototype.valueOf.call([]) // []


当我们创建一个对象 const obj = {},当我们调用 obj.valueOf() 时,访问的就是 Object.prototype.valueOf() 方法。

但是,JavaScript 里面内置了很多全局性的对象,如 ArrayBooleanDateFunctionNumberObjectString。它们都重写了自己的 valueOf 方法。其中 MathError 对象没有 valueOf 方法。

通过以下方式,可以判断一个内置对象是否有重写自己的 valueOf 方法:

// 结果为 false 表示有重写(toString 同理)
Array.prototype.valueOf === Object.prototype.valueOf // true
Function.prototype.valueOf === Object.prototype.valueOf // true
Boolean.prototype.valueOf === Object.prototype.valueOf // false
Date.prototype.valueOf === Object.prototype.valueOf // false
Number.prototype.valueOf === Object.prototype.valueOf // false
String.prototype.valueOf === Object.prototype.valueOf // false
Symbol.prototype.valueOf === Object.prototype.valueOf // false
BigInt.prototype.valueOf === Object.prototype.valueOf // false
// 还有很多内置对象没列出来,可自行翻查 MDN 或 ECMAScript 文档...


对象 返回值
Boolean 返回布尔值。
Date 返回的时间是从 1970 年 1 月 1 日 00:00:00 开始计的毫秒数(UTC)。
Number 返回数字值。
String 字符串值。
Object 返回对象本身。这是默认情况。


需要注意的是,MathError 对象没有 valueOf() 方法。


假设我们自行创建一个对象,可以这样去覆盖默认的 Object.prototype.valueOf() 方法:

// 构造函数
function MyObject(my) {
  this.my = my
}
// 在原型上定义 valueOf 方法(该方法不应传入参数)
MyObject.prototype.valueOf = function() {
  return this.my
}
// 实例化
var myObj = new Object('This is myObj.')
console.log('' + myObj) // "This is myObj."


2. 其他内置对象的 valueOf 方法


其实好像没什么好说的,放链接自己看吧。


二、toString


同样的,一般比较少主动去调用 toString() 方法。


1. Object.prototype.toString()


Object.prototype.toString() 返回一个表示该对象的字符串。

它实际访问的是对象内部的 [[Class]] 属性,返回的形式如:"[object type]",常用于检测对象类型。

function getClass(x) {
  const { toString } = Object.prototype
  const str = toString.call(x)
  return /^\[object (.*)\]$/.exec(str)[1]
}
getClass(null) // "Null"
getClass(undefined) // "Undefined"
getClass({}) // "Object"
getClass([]) // "Array"
getClass(JSON) // "JSON"
getClass(() => {}) // "Function"
;(function() { return getClass(arguments) })() // "Arguments"


Array.prototype.toString === Object.prototype.toString // false
Function.prototype.toString === Object.prototype.toString // false
Boolean.prototype.toString === Object.prototype.toString // false
Date.prototype.toString === Object.prototype.toString // false
Number.prototype.toString === Object.prototype.toString // false
String.prototype.toString === Object.prototype.toString // false
Symbol.prototype.toString === Object.prototype.toString // false
BigInt.prototype.toString === Object.prototype.toString // false


2. Array.prototype.toString()


Array.prototype.toString() 方法,返回一个包含用逗号 , 分隔的每个数组元素的字符串。

var arr = [1, 2, 3]
console.log(arr.toString()) // "1,2,3"
// 结果相当于 Array.prototype.join.call(instance, ',')
arr.join(',') // "1,2,3"


3. Function.prototype.toString()


Function.prototype.toString() 方法,返回一个表示当前函数源代码的字符串。

function fn() {}
console.log(fn.toString()) // "function fn() {}"


4. Boolean.prototype.toString()


Boolean.prototype.toString() 方法,返回指定的布尔对象的字符串形式。

console.log(true.toString()) // "true"
console.log(false.toString()) // "false"


5. String.prototype.toString()


String.prototype.toString() 方法,返回指定对象的字符串形式。

console.log(new String('foo').toString()) // "foo"


6. Symbol.prototype.toString()


Symbol.prototype.toString() 方法,返回当前 Symbol 对象的字符串表示。

需要注意的是,Symbol 原始值不能转换为字符串,只能将其转换成对应的包装对象,再调用 toString() 方法。

console.log(Symbol('foo') + 'bar' ) // TypeError: Cannot convert a Symbol value to a string
// Symbol('foo') 结果是 Symbol 的原始值,再调用其包装对象的属性时,会自动转化为包装对象再调用其 toString() 方法
console.log(Symbol('foo').toString() + 'bar' ) // "Symbol(foo)bar"
7. BigInt.prototype.toString()


BigInt.prototype.toString() 方法,返回一个字符串,后面的 n 不是字符串的一部分。

console.log(1024n.toString()) // "1024"
console.log(1024n.toString(2)) // "10000000000"
console.log(1024n.toString(16)) // "400"


The end.


目录
相关文章
|
16天前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
29天前
|
缓存 监控 前端开发
JavaScript 实现大文件上传的方法
【10月更文挑战第17天】通过以上步骤和方法,我们可以实现较为可靠和高效的大文件上传功能。当然,具体的实现方式还需要根据实际的应用场景和服务器要求进行调整和优化。
|
2天前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
|
16天前
|
JavaScript 前端开发 索引
js中DOM的基础方法
【10月更文挑战第31天】这些DOM基础方法是操作网页文档结构和实现交互效果的重要工具,通过它们可以动态地改变页面的内容、样式和行为,为用户提供丰富的交互体验。
|
16天前
|
缓存 JavaScript UED
js中BOM中的方法
【10月更文挑战第31天】
|
4天前
|
JavaScript 前端开发
js中的bind,call,apply方法的区别以及用法
JavaScript中,`bind`、`call`和`apply`均可改变函数的`this`指向并传递参数。其中,`bind`返回一个新函数,不立即执行;`call`和`apply`则立即执行,且`apply`的参数以数组形式传递。三者在改变`this`指向及传参上功能相似,但在执行时机和参数传递方式上有所区别。
|
15天前
|
JavaScript 前端开发
.js方法参数argument
【10月更文挑战第26天】`arguments` 对象为JavaScript函数提供了一种灵活处理参数的方式,能够满足各种不同的参数传递和处理需求,在实际开发中具有广泛的应用价值。
34 7
|
16天前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
22天前
|
JavaScript 前端开发 Go
异步加载 JS 的方法
【10月更文挑战第24天】异步加载 JavaScript 是提高网页性能和用户体验的重要手段。通过使用不同的方法和技术,可以实现灵活、高效的异步加载 JavaScript。在实际应用中,需要根据具体情况选择合适的方法,并注意处理可能出现的问题,以确保网页能够正常加载和执行。
|
16天前
|
JavaScript 前端开发 开发者
下一篇
无影云桌面