在JavaScript中更改对象类型的名称是否可能?

7

例如:

Object.prototype.toString.call(new Date); // [object Date]
Object.prototype.toString.call(new Array); // [object Array]
Object.prototype.toString.call(new Object); // [object Object]

现在考虑以下内容:
var PhoneNumber = function(number) {
    this.number = number;
}

我可以重写toString()方法,但它并不能真正地更改对象类型。它只是有点欺骗性:

PhoneNumber.prototype.toString = function() {
    return '[object PhoneNumber]';
}

new PhoneNumber().toString(); // [object PhoneNumber]

然而,高超的黑客知道我的 toString() 方法是虚假的,我是一个新手:

Object.prototype.toString.call(new PhoneNumber); // [object Object]

无论原型上的toString()方法是否被覆盖,我希望得到的结果都是:
 Object.prototype.toString.call(new PhoneNumber); // [object PhoneNumber]

这是可能的吗?

3个回答

6
ECMAScript 2015规范定义了一个著名的"Symbol",可用于控制行为:Symbol.toStringTag。不幸的是,目前没有浏览器支持此功能
一旦被支持,您就可以像这样使用它:
PhoneNumber.prototype[Symbol.toStringTag] = 'PhoneNumber';

这将产生您期望的行为:

new PhoneNumber().toString(); // [object PhoneNumber]
Object.prototype.toString.call(new PhoneNumber); // [object PhoneNumber]

V8 release 4.9 开始,该功能已经可用,因此它应该在今年传递到 Node 和 Chrome。 - Ben Harold

2

是的。但是让我先解释一下问题:您没有使用您自己类的.toString(),而是使用了ObjecttoString(),它有它自己的定义,并且不返回您对象的toString()

我为您提供两个解决方案*:(在Edge、Chrome、Firefox和IE9-11中都经过测试)

解决方案 A

Object.prototype.toString = (function(f) {
  return function() {
    return (this.toString === Object.prototype.toString
            || /\{\s*\[native code\]\s*\}/.test(this.toString.toString())
           ) ? f.call(this) : this.toString()
  }
})(Object.prototype.toString) // or just {}.toString


var PhoneNumber = function(number) {
  this.number = number;
}

PhoneNumber.prototype.toString = function() {
  return '[object PhoneNumber]';
}

document.writeln(Object.prototype.toString.call(new PhoneNumber))
document.writeln(Object.prototype.toString.call(Function()))
document.writeln(Object.prototype.toString.call(Array()))
document.writeln(Object.prototype.toString.call(window))
document.writeln(Object.prototype.toString.call(new Date))
document.writeln(Object.prototype.toString.call(1))
document.writeln(Object.prototype.toString.call(null))
document.writeln(Object.prototype.toString.call(""))
document.writeln(Object.prototype.toString.call(document))
document.writeln(Object.prototype.toString.call(document.createElement("div")))
document.writeln(Object.prototype.toString.call(Object()))
document.writeln(({}).toString()) // check

方案B

Object.prototype.toString = (function(f) {
  return function() {
    return (typeof this._toString ===  'undefined') ? f.call(this) : this._toString()
  }
})(Object.prototype.toString)


var PhoneNumber = function(number) {
  this.number = number;
}

PhoneNumber.prototype._toString = function() {
  return '[object PhoneNumber]';
}

document.writeln(Object.prototype.toString.call(new PhoneNumber))
document.writeln(Object.prototype.toString.call(Function()))
document.writeln(Object.prototype.toString.call(Array()))
document.writeln(Object.prototype.toString.call(window))
document.writeln(Object.prototype.toString.call(new Date))
document.writeln(Object.prototype.toString.call(1))
document.writeln(Object.prototype.toString.call(null))
document.writeln(Object.prototype.toString.call(""))
document.writeln(Object.prototype.toString.call(document))
document.writeln(Object.prototype.toString.call(document.createElement("div")))
document.writeln(Object.prototype.toString.call(Object()))
document.writeln(({}).toString()) // check

  • 我只花了两个小时弄明白它。

2
这会破坏通用对象的toString方法:({}).toString()将导致调用堆栈超出错误。 - Noah Freitas
@NoahFreitas 已解决该问题。 - CoderPi

0
因为Object.prototype.toString返回类的[[Class]]内部属性,而[[Class]]内部属性的值由es5规范为每种内置对象(如Date、Regexp等)定义。在规范中,您找不到任何访问此属性的方法。该属性用于设置内置对象之间的差异,因此覆盖toString无法更改[[Class]]属性,并且结果始终为[object Object]。更详细的toString工作原理请参见http://es5.github.io/#x15.2.4.2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接