【JavaScript】手写 Object.prototype.toString()
🔧 一、核心实现原理
-
内部逻辑
- 访问对象的
Symbol.toStringTag
属性(ES6+)或内部[[Class]]
属性(ES5 及之前)。 - 若属性不存在,根据对象类型推断默认标签(如
"Object"
、"Array"
)。 - 返回拼接字符串
"[object " + tag + "]"
。
- 访问对象的
-
关键特性
- 不可直接调用:需通过
call/apply
绑定this
指向目标对象。 - 优先级:
Symbol.toStringTag
> 内置[[Class]]
属性。
- 不可直接调用:需通过
✨ 二、完整代码实现
Object.defineProperty(Object.prototype, 'toString', {value: function() {// 1. 处理 null 和 undefinedif (this === null) return '[object Null]';if (this === undefined) return '[object Undefined]';// 2. 获取 Symbol.toStringTag 或内部 [[Class]]const tag = Symbol.toStringTag in Object(this) ? this[Symbol.toStringTag] : Object.prototype.toString.getInternalClass(this);// 3. 返回格式化字符串return `[object ${tag}]`;},writable: true,configurable: true
});// 辅助方法:模拟内部 [[Class]] 检测
Object.prototype.toString.getInternalClass = (obj) => {if (Array.isArray(obj)) return 'Array';if (obj instanceof Date) return 'Date';if (obj instanceof RegExp) return 'RegExp';// 其他内置类型判断...return 'Object'; // 默认值
};
🧪 三、功能验证
1. 基本类型检测
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(null)); // [object Null]
2. 自定义类型标签
class MyClass {get [Symbol.toStringTag]() { return 'MyClass'; }
}
console.log(Object.prototype.toString.call(new MyClass())); // [object MyClass]
3. 避免重写影响
const arr = [];
arr.toString = () => "Overridden!";
console.log(Object.prototype.toString.call(arr)); // 仍输出 [object Array]
⚠️ 四、注意事项
-
兼容性处理
- 旧版浏览器(如 IE)需手动实现
Symbol.toStringTag
的Polyfill
。 - 内部
[[Class]]
在 ES5 后不再直接暴露,需通过类型推断模拟。
- 旧版浏览器(如 IE)需手动实现
-
性能优化
- 避免频繁调用:每次调用涉及原型链查找和字符串拼接,对性能敏感场景慎用。
-
与重写方法的区别
- 直接调用
obj.toString()
可能被重写逻辑覆盖,而Object.prototype.toString.call(obj)
始终访问原始方法。
- 直接调用
💡 五、应用场景
-
精准类型判断
替代typeof
和instanceof
,统一处理基本类型和对象类型:function getType(obj) {return Object.prototype.toString.call(obj).slice(8, -1); } getType(new Map()); // "Map"
-
自定义对象描述
通过Symbol.toStringTag
控制调试信息输出:class NetworkError {get [Symbol.toStringTag]() { return 'NetworkError'; } } console.log(new NetworkError() + ""); // [object NetworkError]