是否可以重写JavaScript的toString()函数以提供有意义的调试输出?


115

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

在C#中,我习惯于重写ToString()以能够自定义对象的调试器表示形式。我可以用JavaScript做类似的事情吗?


2
我发现输出是告诉您变量拥有什么(或至少比更好typeof)的最可靠方法。
alex

Answers:


102

您也可以toString在Javascript中覆盖。参见示例:

function Foo() 
{
}

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

var f = new Foo();
alert(f);  // popup displays [object Foo]

对如何在JavaScript中确定对象类型名称的讨论。


8
的确,alert函数将显示覆盖原型toString属性的函数返回值,Object.prototype.toString.call(f)并仍显示[object Object]
Frederik Krautwald

14
'Object.prototype.toString.call(f)仍将显示[object Object]。是的,因为这与'Foo.prototype.toString'完全不同,哈哈。
Triynko

5
如果像我这样的人到此为止,则可以在ES6中使用Sybmol.toStringTag来自定义Object.prototype.toString.call行为。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
TLadd

32

首先覆盖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'

5
console.log(''+ foo); 这是问题,直到达到您的答案,我才看到任何toString实现。
ahmadalibaloch 2015年

13

在浏览器JS中获得可调试输出的一种简单方法是将对象序列化为JSON。所以你可以打个电话

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

因此,例如,alert("Blah! " + JSON.stringify({key: "value"}));产生一个带有文本的警报Blah! {"key":"value"}


那很方便。我想象中的输出可能会很大,但是可以正常工作!
devios1 2011年

@dev方便,但不会覆盖toString()。
Dan Dascalescu

10

如果您正在使用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 }

当没有检查的版本打印时:

{ x: 1, y: 2 }

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}"

9
这段代码无法解决OP的console.log问题,至少在node.js v0.10.*或Chrome中无法解决Version 32.0.1700.102。尽管直接调用(字符串)(字符串)或使用类型强制(字符串)的toString可以与此一起使用,但console [/ info | log /]用于旧的预修改toString。
james_womack 2014年

1
现在是2019年,nodejs和chrome漂亮的打印对象都是它们自己的,因此强制性(将对象添加到字符串中时)是您会用谷歌搜索这个问题的唯一用例。
克莱森

6

如果对象是您自己定义的,则始终可以添加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);


5

使用模板文字

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

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

3

添加“ 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]

一些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

0

Chrome控制台日志可让您检查对象。


是的,如果我仅输出对象,那是对的,这很方便。但是有时我只想将其作为字符串的一部分输出,可以用来包含其他数据,如果我可以以某种方式自定义该形式,那会很好。
devios1 2011年

6
我刚刚发现您可以在console.log中使用其他参数来输出以字符串内联的对象:console.log("this is my object:", obj)
devios1 2012年

0

-此操作需要花费大量时间才能完成,并且根据mozilla docs不建议使用此方法:https : //developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/proto

-显然,现代的浏览器已弃用.prototype,而ECMA6则改为使用proper____proto__来指定。

因此,例如,如果要定义自己的对象地理区域,则应调用__proto__属性而不是.prototype

var  geoposition = {

        lat: window.pos.lat,
        lng: window.pos.lng
    };

geoposition.__proto__.toString = function(){ return "lat: "+this.lat+", lng: "+this.lng }
console.log("Searching nearby donations to: "+geoposition.toString());

0

以下是如何对Map对象进行字符串化的示例:

  Map.prototype.toString = function() {

    let result = {};

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

    return JSON.stringify(result);
  };

-1

您可以给任何自定义对象自己的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);
}

-1

toString()如果您包括Prototype JavaScript Library,则可以覆盖,而不是重写,它可以Object.inspect()用来获得更有用的表示形式。

最流行的框架包括类似的东西。


-1

您可以在JS中扩展或覆盖

String.prototype.toString = function() {
    return this + "..."
}
document.write("Sergio".toString());


这如何在提供相同解决方案的2011年答案中添加任何内容?
Dan Dascalescu

-3
A simple format Date function using Javascript prototype, it can be used for your purpose

https://gist.github.com/cstipkovic/3983879 :

Date.prototype.formatDate = function (format) {
    var date = this,
        day = date.getDate(),
        month = date.getMonth() + 1,
        year = date.getFullYear(),
        hours = date.getHours(),
        minutes = date.getMinutes(),
        seconds = date.getSeconds();

    if (!format) {
        format = "MM/dd/yyyy";
    }

    format = format.replace("MM", month.toString().replace(/^(\d)$/, '0$1'));

    if (format.indexOf("yyyy") > -1) {
        format = format.replace("yyyy", year.toString());
    } else if (format.indexOf("yy") > -1) {
        format = format.replace("yy", year.toString().substr(2, 2));
    }

    format = format.replace("dd", day.toString().replace(/^(\d)$/, '0$1'));

    if (format.indexOf("t") > -1) {
        if (hours > 11) {
            format = format.replace("t", "pm");
        } else {
            format = format.replace("t", "am");
        }
    }

    if (format.indexOf("HH") > -1) {
        format = format.replace("HH", hours.toString().replace(/^(\d)$/, '0$1'));
    }

    if (format.indexOf("hh") > -1) {
        if (hours > 12) {
            hours -= 12;
        }

        if (hours === 0) {
            hours = 12;
        }
        format = format.replace("hh", hours.toString().replace(/^(\d)$/, '0$1'));
    }

    if (format.indexOf("mm") > -1) {
        format = format.replace("mm", minutes.toString().replace(/^(\d)$/, '0$1'));
    }

    if (format.indexOf("ss") > -1) {
        format = format.replace("ss", seconds.toString().replace(/^(\d)$/, '0$1'));
    }

    return format;
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.