细读 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天前
|
JavaScript 前端开发
JavaScript基础知识-方法
文章通过示例代码讲解了JavaScript中如何给对象添加方法以及如何调用这些方法。
18 2
JavaScript基础知识-方法
|
16天前
|
JavaScript 前端开发
JavaScript基础知识-数组的常用方法
关于JavaScript基础知识-数组的常用方法。
14 1
JavaScript基础知识-数组的常用方法
|
16天前
|
JavaScript 前端开发
JavaScript基础知识-toString()
关于JavaScript基础知识中toString()方法的介绍。
21 1
JavaScript基础知识-toString()
|
1天前
|
JavaScript 前端开发
JavaScript Array map() 方法
JavaScript Array map() 方法
|
21天前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
21天前
|
JavaScript 算法 前端开发
JS算法必备之Array常用操作方法
这篇文章详细介绍了JavaScript中数组的创建、检测、转换、排序、操作方法以及迭代方法等,提供了数组操作的全面指南。
JS算法必备之Array常用操作方法
|
1天前
|
存储 JavaScript
js切割截取字符串方法
js切割截取字符串方法
|
1天前
|
JavaScript
JS数组合并的常用方法
JS数组合并的常用方法
|
14天前
|
前端开发 JavaScript
前端ES5 | js —添加元素方法
前端ES5 | js —添加元素方法
|
19天前
|
Java 开发者 关系型数据库
JSF与AWS的神秘之旅:如何在云端部署JSF应用,让你的Web应用如虎添翼?
【8月更文挑战第31天】在云计算蓬勃发展的今天,AWS已成为企业级应用的首选平台。本文探讨了在AWS上部署JSF(JavaServer Faces)应用的方法,这是一种广泛使用的Java Web框架。通过了解并利用AWS的基础设施与服务,如EC2、RDS 和 S3,开发者能够高效地部署和管理JSF应用。文章还提供了具体的部署步骤示例,并讨论了使用AWS可能遇到的挑战及应对策略,帮助开发者更好地利用AWS的强大功能,提升Web应用开发效率。
42 0