探究object.prototype.toString.call()方法的实现原理

本文探讨了JavaScript中利用`Object.prototype.toString.call()`方法来获取对象类型的原因和原理。当对象的toString方法被重写时,通过call方法可以调用原始版本。参数处理涉及到null和undefined的特殊情况,以及基本数据类型的转换。关键在于获取对象的[Symbol.toStringTag]属性,该属性可能在原型链上,用于确定类型标签。文章建议使用此方法进行精确的数据类型检测,因为它比typeof和instanceof有更清晰的判断结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Object.prototype.toString.call(1);  '[object Number]'

Object.prototype.toString.call(NaN); '[object Number]'

Object.prototype.toString.call('1'); '[object String]'

Object.prototype.toString.call(true); '[object Boolean]'

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

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

Object.prototype.toString.call(Symbol());'[object Symbol]'

Object.prototype.toString.call(foo);  '[object Function]'

Object.prototype.toString.call([1,2,3]); '[object Array]'

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

为什么要使用call

对于 Object.prototype.toString() 方法,会返回一个形如 “[object XXX]” 的字符串。

如果对象的 toString()方法未被重写,就会返回形如上面形式的字符串。

({}).toString();     // => "[object Object]"
Math.toString();     // => "[object Math]"

但是,大多数对象对于toString()方法都是重写了的,因此这时需要使用 call()方法来调用未被重写前的toString方法。

var x = {
  toString() {
    return "X";
  },
};

x.toString();                                     // => "X"

Object.prototype.toString.call(x);                // => "[object Object]"

参数问题

对于 Object.prototype.toString.call(arg),若参数为 null 或 undefined,则直接返回结果。

Object.prototype.toString.call(null);       // => "[object Null]"
Object.prototype.toString.call(undefined);  // => "[object Undefined]"

若参数不为 null 或 undefined,则将参数先转为对象,再作判断。对于基本数据类型,转为对象的方法即包装类。

转为对象后,便会取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,如果没有该属性或者该属性值不为字符串类型,则依下表取得 tag, 然后返回 "[object " + tag + “]” 形式的字符串,而这个字符串便可以显示的告诉你数据是哪种类型,不会像typeof方法那样无法准确区分数组和对象,也不会像instanceof方法那样去猜测数据是哪种类型。

// Boolean 类型,tag 为 "Boolean"
Object.prototype.toString.call(true);            // => "[object Boolean]"

// Number 类型,tag 为 "Number"
Object.prototype.toString.call(1);               // => "[object Number]"

// String 类型,tag 为 "String"
Object.prototype.toString.call("");              // => "[object String]"

// Array 类型,tag 为 "String"
Object.prototype.toString.call([]);              // => "[object Array]"

// Arguments 类型,tag 为 "Arguments"
Object.prototype.toString.call((function() {
  return arguments;
})());                                           // => "[object Arguments]"

// Function 类型, tag 为 "Function"
Object.prototype.toString.call(function(){});    // => "[object Function]"

// Error 类型(包含子类型),tag 为 "Error"
Object.prototype.toString.call(new Error());     // => "[object Error]"

// RegExp 类型,tag 为 "RegExp"
Object.prototype.toString.call(/\d+/);           // => "[object RegExp]"

// Date 类型,tag 为 "Date"
Object.prototype.toString.call(new Date());      // => "[object Date]"

// 其他类型,tag 为 "Object"
Object.prototype.toString.call(new class {});    // => "[object Object]"

下面为部署了 Symbol.toStringTag 的例子。可以看出,属性值期望是一个字符串,否则会被忽略。

var o1 = { [Symbol.toStringTag]: "A" };
var o2 = { [Symbol.toStringTag]: null };

Object.prototype.toString.call(o1);      // => "[object A]"
Object.prototype.toString.call(o2);      // => "[object Object]"

当然了,Symbol.toStringTag 属性也是可以部署在原型链上的:

class A {}
A.prototype[Symbol.toStringTag] = "A";
Object.prototype.toString.call(new A());   // => "[object A]"

(而symbol这种基本数据类型也正是因为在其原型链上部署了这个属性,因而能正确通过此方法进行正确数据类型判断)

console.log(Object.prototype.toString.call(Symbol())); //"[object Symbol]"

以上便是此方法的基本原理实现。综合以上,我更推荐大家使用这个方法去准确显示的检测数据类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值