可以覆盖JavaScript的toString()函数以提供有意义的调试输出吗?

149

当我在JavaScript程序中使用console.log()输出一个对象时,我只会看到输出[object Object],这对于找出是什么对象(甚至是什么类型的对象)并不是很有帮助。

在C#中,我习惯于重写ToString()以便能够自定义对象的调试器表示。在JavaScript中是否有类似的方法?


2
我发现输出是告诉你一个变量存储了什么内容最可靠的方式(或者至少比 typeof 更好)。 - alex
16个回答

123
你也可以在JavaScript中覆盖toString方法。 请参见以下示例:

function Foo() {}

// toString override added to prototype of Foo class
Foo.prototype.toString = function() {
  return "[object Foo]";
}

var f = new Foo();
console.log("" + f); // console displays [object Foo]

参见此讨论,学习如何在JavaScript中确定对象类型名称。


9
虽然alert函数将显示函数的返回值并覆盖原型中的toString属性,但Object.prototype.toString.call(f)仍将显示[object Object] - Frederik Krautwald
25
"Object.prototype.toString.call(f)" 仍将显示 "[object Object]"。这是因为它是完全不同的函数,与 "Foo.prototype.toString" 不同,哈哈。 - Triynko
12
如果有其他像我一样的人来到这里,你可以使用ES6中的Symbol.toStringTag来自定义Object.prototype.toString.call行为。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag - TLadd

47

为你的对象或原型编写toString的重写方法:

var Foo = function(){};
Foo.prototype.toString = function(){return 'Pity the Foo';};

var foo = new Foo();

然后将其转换为字符串以查看对象的字符串表示:

//using JS implicit type conversion
console.log('' + foo);

如果你不喜欢多余的打字,你可以创建一个函数,将其参数的字符串表示记录到控制台:

var puts = function(){
    var strings = Array.prototype.map.call(arguments, function(obj){
        return '' + obj;
    });
    console.log.apply(console, strings);
};

使用方法:

puts(foo)  //logs 'Pity the Foo'

puts(foo, [1,2,3], {a: 2}) //logs 'Pity the Foo 1,2,3 [object Object]'

更新

E2015提供了更好的语法,但是你需要使用像Babel这样的转译器:

// override `toString`
class Foo {
  toString(){
    return 'Pity the Foo';
  }
}

const foo = new Foo();

// utility function for printing objects using their `toString` methods
const puts = (...any) => console.log(...any.map(String));

puts(foo); // logs 'Pity the Foo'

9
console.log('' + foo); 这就是问题所在,直到我看到你的答案才意识到没有看到任何toString方法的实现。 - ahmadalibaloch

17

如果你正在使用Node,考虑使用util.inspect可能是值得的。

var util = require('util')

const Point = {
  x: 1,
  y: 2,
  [util.inspect.custom]: function(depth) { return `{ #Point ${this.x},${this.y} }` }

}

console.log( Point );

这将产生:

{ #Point 1,2 }

没有 inspect 版本打印的是:

{ x: 1, y: 2 }

更多信息(+中使用的示例):

https://nodejs.org/api/util.html#util_util_inspect_custom


17

给自定义对象或类添加'Symbol.toStringTag'属性。

分配给它的字符串值将成为其默认字符串描述,因为它在内部被Object.prototype.toString()方法访问。

例如:

class Person {
  constructor(name) {
    this.name = name
  }
  get [Symbol.toStringTag]() {
    return 'Person';
  }
}

let p = new Person('Dan');
Object.prototype.toString.call(p); // [object Person]

class Person {
  constructor(name) {
    this.name = name
  }
  get[Symbol.toStringTag]() {
    return 'Person';
  }
}

let p = new Person('Dan');
console.log(Object.prototype.toString.call(p));

一些JavaScript类型(例如Maps和Promises)已定义了内置的toStringTag符号

Object.prototype.toString.call(new Map());       // "[object Map]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"

由于Symbol.toStringTag是一个众所周知的符号,我们可以引用它并验证上述类型确实具有Symbol.toStringTag属性 -

new Map()[Symbol.toStringTag] // 'Map'
Promise.resolve()[Symbol.toStringTag] // 'Promise'

这是与直接覆盖 toString() 一起实现 function MyObj() {} Object.prototype.toString.call(new MyObj()) // "[object MyObj]" 的唯一方法吗? - tonix
1
@tonix - 我也这么认为... 如果有其他方法,请告诉我 ;) - Danield
1
这应该是任何在过去三到四年中编写的代码的被接受答案。此外,在单元测试中模拟类时非常有用,因为您可以检查您正在实例化的对象类型。 - David G

14

在浏览器JS中获取可调试的输出的简单方法是将对象序列化为JSON。因此,您可以进行如下调用:

console.log ("Blah: " + JSON.stringify(object));

以一个例子来说,alert("Blah! " + JSON.stringify({key: "value"})); 会产生一个带有文本内容 Blah! {"key":"value"} 的警告框。


这非常方便。我想输出可能会有点巨大,但在紧急情况下很有效! - devios1
@dev 方便,但不会覆盖 toString() 方法。 - Dan Dascalescu
只要小心循环引用和函数即可。 - Jelle Blaauw

11

使用模板字符串

class Foo {
  toString() {
     return 'I am foo';
  }
}

const foo = new Foo();
console.log(`${foo}`); // 'I am foo'

7
如果对象是由您自己定义的,您总是可以添加toString覆盖。

//Defined car Object
var car = {
  type: "Fiat",
  model: 500,
  color: "white",
  //.toString() Override
  toString: function() {
    return this.type;
  }
};

//Various ways to test .toString() Override
console.log(car.toString());
console.log(car);
alert(car.toString());
alert(car);

//Defined carPlus Object
var carPlus = {
  type: "Fiat",
  model: 500,
  color: "white",
  //.toString() Override
  toString: function() {
    return 'type: ' + this.type + ', model: ' + this.model + ', color:  ' + this.color;
  }
};

//Various ways to test .toString() Override
console.log(carPlus.toString());
console.log(carPlus);
alert(carPlus.toString());
alert(carPlus);


6

只需重写toString()方法即可。

简单示例:

var x = {foo: 1, bar: true, baz: 'quux'};
x.toString(); // returns "[object Object]"
x.toString = function () {
    var s = [];
    for (var k in this) {
        if (this.hasOwnProperty(k)) s.push(k + ':' + this[k]);
    }
    return '{' + s.join() + '}';
};
x.toString(); // returns something more useful

如果您定义了一个新类型,它的表现会更好:

function X()
{
    this.foo = 1;
    this.bar = true;
    this.baz = 'quux';
}

X.prototype.toString = /* same function as before */

new X().toString(); // returns "{foo:1,bar:true,baz:quux}"

11
这段代码并没有解决原帖中的 console.log 问题,至少在 node.js v0.10.* 或 Chrome Version 32.0.1700.102 中不行。虽然直接调用 toString (笨拙) 或使用类型强制转换 (更糟) 可以解决这个问题,但是 console[/info|log/] 使用了旧版的未改良的 toString。 - james_womack
1
现在是2019年,Nodejs和Chrome都可以自己漂亮打印对象,因此当你将对象添加到字符串时强制转换是唯一的用例,我相信这是你会搜索这个问题的原因。 - Klesun
@Kleson 人们并不总是寻找可能的事情,而是他们梦想中的可能。偶尔答案是“你不能”。 - Ryan Leach

0
你可以为任何自定义对象编写自己的toString方法,或者编写一个通用的方法,可在查看的对象上调用。
Function.prototype.named= function(ns){
    var Rx=  /function\s+([^(\s]+)\s*\(/, tem= this.toString().match(Rx) || "";
    if(tem) return tem[1];
    return 'unnamed constructor'
}

function whatsit(what){
    if(what===undefined)return 'undefined';
    if(what=== null) return 'null object';
    if(what== window) return 'Window object';
    if(what.nodeName){
        return 'html '+what.nodeName;
    }
    try{
        if(typeof what== 'object'){
            return what.constructor.named();
        }
    }
    catch(er){
        return 'Error reading Object constructor';
    }
    var w=typeof what;
    return w.charAt(0).toUpperCase()+w.substring(1);
}

0

这里是一个将 Map 对象转换为字符串的示例:

  Map.prototype.toString = function() {

    let result = {};

    this.forEach((key, value) => { result[key] = value;});

    return JSON.stringify(result);
  };

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