谈谈 Object.prototype.toString 。

本文详细探讨了Object.prototype.toString方法在不同ECMAScript版本中的实现细节。从ECMAScript5到ECMAScript6及之后的变化,包括内部属性[[Class]]的使用到内部槽位的引入。同时介绍了如何利用该方法来判断变量类型。
部署运行你感兴趣的模型镜像

原文链接我的blog

前几日看到一个比较熟悉的面试题,判断一个变量是不是数组?
以下几种方法供参考:

var arr = [1, 2, 3]
Array.isArray(arr)
arr instanceof Array
arr.constructor === Array
Object.prototype.toString.call(arr) === '[object Array]'
...

这篇文章主要是谈谈 Object.prototype.toString

ECMAScript 5

在ECMAScript 5中,Object.prototype.toString()被调用时,会进行如下步骤:
- 如果 thisundefined ,返回 [object Undefined]
- 如果 thisnull , 返回 [object Null]
- 令 O 为以 this 作为参数调用 ToObject 的结果;
- 令 classO 的内部属性 [[Class]] 的值;
- 返回三个字符串 "[object", class, 以及"]" 拼接而成的字符串。

[[Class]]

[[Class]]是一个内部属性,值为一个类型字符串,可以用来判断值的类型。

有这么一段详细的解释:

本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 “Arguments”, “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”, “String” 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。注,本规范中除了通过 Object.prototype.toString ( 见 15.2.4.2) 没有提供任何手段使程序访问此值。

在JavaScript代码里,唯一可以访问该属性的方法就是通过 Object.prototype.toString ,通常方法如下:

Object.prototype.toString.call(value)

举例:

> Object.prototype.toString.call(null)
'[object Null]'

> Object.prototype.toString.call(undefined)
'[object Undefined]'

> Object.prototype.toString.call(Math)
'[object Math]'

> Object.prototype.toString.call({})
'[object Object]'

> Object.prototype.toString.call([])
'[object Array]'

因此,可以用下列函数,来获取任意变量的[[Class]]属性:

function getClass (a) {
  const str = Object.prototype.toString.call(a)
  return /^\[object (.*)\]$/.exec(str)[1]
}

运行即可得

> getClass(null)
'Null'

> getClass(undefined)
'Undefined'

> getClass(Math)
'Math'

> getClass({})
'Object'

> getClass([])
'Array'

ECMAScript 6

在ES6,调用 Object.prototype.toString 时,会进行如下步骤:
- 如果 thisundefined ,返回 '[object Undefined]' ;
- 如果 thisnull , 返回 '[object Null]'
- 令 O 为以 this 作为参数调用 ToObject 的结果;
- 令 isArrayIsArray(O)
- ReturnIfAbrupt(isArray) (如果 isArray 不是一个正常值,比如抛出一个错误,中断执行);
- 如果 isArraytrue , 令 builtinTag'Array' ;
- else ,如果 O is an exotic String object , 令 builtinTag'String'
- else ,如果 O 含有 [[ParameterMap]] internal slot, , 令 builtinTag'Arguments'
- else ,如果 O 含有 [[Call]] internal method , 令 builtinTagFunction
- else ,如果 O 含有 [[ErrorData]] internal slot , 令 builtinTagError
- else ,如果 O 含有 [[BooleanData]] internal slot , 令 builtinTagBoolean
- else ,如果 O 含有 [[NumberData]] internal slot , 令 builtinTagNumber
- else ,如果 O 含有 [[DateValue]] internal slot , 令 builtinTagDate
- else ,如果 O 含有 [[RegExpMatcher]] internal slot , 令 builtinTagRegExp
- else , 令 builtinTagObject
- 令 tagGet(O, @@toStringTag) 的返回值( Get(O, @@toStringTag) 方法,既是在 O 是一个对象,并且具有 @@toStringTag 属性时,返回 O[Symbol.toStringTag] );
- ReturnIfAbrupt(tag) ,如果 tag 是正常值,继续执行下一步;
- 如果 Type(tag) 不是一个字符串,let tag be builtinTag
- 返回由三个字符串 "[object", tag, and "]" 拼接而成的一个字符串。

在ES6里,之前的 [[Class]] 不再使用,取而代之的是一系列的 internal slot ,有一个比较完整的解释:

Internal slots correspond to internal state that is associated with objects and used by various ECMAScript specification algorithms. Internal slots are not object properties and they are not inherited. Depending upon the specific internal slot specification, such state may consist of values of any ECMAScript language type or of specific ECMAScript specification type values

大概的意思是:Internal slots 对应于与对象相关联并由各种ECMAScript规范算法使用的内部状态,它们没有对象属性,也不能被继承,根据具体的 Internal slot 规范,这种状态可以由任何ECMAScript语言类型或特定ECMAScript规范类型值的值组成。

此外,通过对 Object.prototype.toString 在ES6的实现步骤分析,我们其实可以很容易改变 Object.prototype.toString.call 的结果,像下面一样:

let obj = {}

Object.defineProperty(obj, Symbol.toStringTag, {
    get: function() {
        return "newClass"
    }
})

console.log(Object.prototype.toString.call(obj)) // "[object newClass]"

ECMAScript 7

ES7目前还是工作草案,到目前为止,就 Object.prototype.toString 的实现步骤来说, 只是移除了ES6其中的 ReturnIfAbrupt

参考

  1. http://www.ecma-international.org/ecma-262/5.1
  2. http://www.adobe.com/devnet/archive/html5/articles/categorizing-values-in-javascript.html
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
  4. http://www.ecma-international.org/ecma-262/6.0/
  5. http://es6.ruanyifeng.com/#docs/symbol
  6. https://tc39.github.io/ecma262/#sec-object.prototype.tostring

完。

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

### 作用 `Object.prototype.toString.call()` 是 JavaScript 中用于检测变量类型的强大工具。它能够准确区分所有 JavaScript 内置类型,包括原始类型(如 `null`、`undefined`、`number`、`string` 等)和对象类型(如 `Array`、`Date`、`Function` 等)[^5]。与直接调用对象的 `toString()` 方法不同,`Object.prototype.toString.call()` 不依赖对象自身的 `toString()` 实现,而是通过传入任意值并返回其类型信息,确保结果的准确性和一致性[^1]。 该方法的核心优势在于其通用性和准确性。例如,使用 `typeof` 检测 `null` 时会返回 `"object"`,而 `Object.prototype.toString.call()` 可以明确返回 `[object Null]`,从而避免歧义[^5]。 ### 使用示例 可以通过以下方式使用 `Object.prototype.toString.call()` 来判断不同类型: ```javascript console.log(Object.prototype.toString.call(null)); // [object Null] console.log(Object.prototype.toString.call(undefined)); // [object Undefined] console.log(Object.prototype.toString.call(123)); // [object Number] console.log(Object.prototype.toString.call('hello')); // [object String] console.log(Object.prototype.toString.call(true)); // [object Boolean] console.log(Object.prototype.toString.call({})); // [object Object] console.log(Object.prototype.toString.call([])); // [object Array] console.log(Object.prototype.toString.call(new Date())); // [object Date] console.log(Object.prototype.toString.call(function() {})); // [object Function] ``` ### 注意事项 虽然 `Object.prototype.toString.call()` 是一种强大的类型检测方法,但它也存在一些不足之处。例如,其语法相对繁琐,需要使用 `.call()` 方法并传入目标值作为上下文,这在现代 JavaScript 开发中显得不够简洁[^2]。此外,返回值是一个字符串,需要进一步解析才能提取类型信息,这可能影响代码的可读性和性能[^2]。 另外,需要注意的是,如果修改了 `Object.prototype.toString` 的指向,可能会影响其行为。例如,将 `Object.prototype.toString` 重写为返回固定值,会导致所有调用该方法的对象返回相同结果[^4]。 ### 与其他方法的对比 - **`typeof`**:无法准确区分 `null` 和对象,也无法识别 `Array` 或 `Date` 等内置对象类型。 - **`instanceof`**:依赖原型链,不能跨环境使用,例如在不同 iframe 中创建的对象可能无法正确识别。 - **`Array.isArray()`**:仅适用于检测数组类型,无法用于检测其他类型[^3]。 相比之下,`Object.prototype.toString.call()` 提供了更全面、可靠的类型检测能力。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值