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

Posted

技术标签:

【中文标题】是否可以覆盖 JavaScript 的 toString() 函数来为调试提供有意义的输出?【英文标题】:Is it possible to override JavaScript's toString() function to provide meaningful output for debugging? 【发布时间】:2011-09-12 13:05:29 【问题描述】:

当我在我的 javascript 程序中console.log() 一个对象时,我只会看到输出 [object Object],这对于确定它是什么对象(甚至是什么类型的对象)没有多大帮助。

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

【问题讨论】:

我发现输出是告诉你变量包含什么的最可靠的方式(或者至少比typeof更好)。 【参考方案1】:

您也可以在 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]

请参阅this 讨论如何在 JavaScript 中确定对象类型名称。

【讨论】:

虽然警报函数会显示覆盖原型toString属性的函数的返回值,但Object.prototype.toString.call(f)仍将显示[object Object] 'Object.prototype.toString.call(f) 仍将显示 [object Object]。'是的,因为这是一个与 'Foo.prototype.toString' 完全不同的函数,哈哈。 如果像我这样的其他人在这里结束,您可以使用 ES6 中的 Sybmol.toStringTag 来自定义 Object.prototype.toString.call 行为。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案2】:

Chrome 控制台日志允许您检查对象。

【讨论】:

是的,如果我只输出对象,那就是真的,这很方便。但是有时我只想将它作为字符串的一部分输出,我可能会使用它来包含其他数据,如果我可以以某种方式自定义该表单会很好。 我刚刚发现您可以在 console.log 中使用其他参数来输出与字符串内联的对象:console.log("this is my object:", obj)【参考方案3】:

只需覆盖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"

【讨论】:

这段代码没有解决 OP 的 console.log 问题,至少在 node.js v0.10.* 或 Chrome Version 32.0.1700.102 中没有。虽然直接调用 toString (lamer) 或使用类型强制 (lamer) 都可以解决这个问题,但 console[/info|log/] 使用旧的 pre-mod toString。 现在是 2019 年,nodejs 和 chrome 都可以自己打印漂亮的对象,所以强制(当你将对象添加到字符串时)是我相信你会用谷歌搜索这个问题的唯一用例。 【参考方案4】:

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

【讨论】:

【参考方案5】:

如果您包含Prototype JavaScript Library,而不是覆盖toString(),您可以使用Object.inspect() 来获得更有用的表示。

大多数流行的框架都包含类似的东西。

【讨论】:

【参考方案6】:

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

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

例如,alert("Blah! " + JSON.stringify(key: "value")); 会生成带有文本 Blah! "key":"value" 的警报

【讨论】:

这很方便。我想象的输出可能有点大,但在紧要关头可以工作! @dev 很方便,但不会覆盖 toString()。 请注意循环引用和函数。【参考方案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);

【讨论】:

【参考方案8】:

首先为您的对象或原型覆盖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'

【讨论】:

console.log('' + foo);这是我在得到您的答案之前没有看到任何 toString 实现的问题。【参考方案9】:
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;
;

【讨论】:

【参考方案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 

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

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

【讨论】:

【参考方案11】:

与template literals:

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


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

【讨论】:

【参考方案12】:

您可以在 JS 中扩展或覆盖

String.prototype.toString = function() 
    return this + "..."

document.write("Sergio".toString());

【讨论】:

这对 2011 年给出相同解决方案的答案有何影响?【参考方案13】:

-这个操作需要很多时间 完整,并且根据 Mozilla 文档不鼓励使用它: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/proto

-显然,现代浏览器不推荐使用 .prototype 和 ECMA6 指定 改用正确的__proto__。

例如,如果您要定义自己的对象 geoposition,则应调用 __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());

【讨论】:

【参考方案14】:

这是一个如何对 Map 对象进行字符串化的示例:

  Map.prototype.toString = function() 

    let result = ;

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

    return JSON.stringify(result);
  ;

【讨论】:

【参考方案15】:

将'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 是well-known symbol,我们可以引用它并验证上述类型确实具有 Symbol.toStringTag 属性-

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

【讨论】:

这是与直接覆盖toString() 一起实现function MyObj() Object.prototype.toString.call(new MyObj()) // "[object MyObj]" 的唯一方法吗? @tonix - 我想是的......如果有其他方法,请告诉我;) 这应该是过去三四年内编写的任何代码的公认答案。顺便说一句,它在单元测试中模拟类时很有用,因此您可以检查您正在实例化的对象的类型。

以上是关于是否可以覆盖 JavaScript 的 toString() 函数来为调试提供有意义的输出?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 函数方法 - toString()

intellij idea中怎么覆盖tostring(),hashcode(),equals()?

Java 对象 始终要覆盖toString

javascript 对象是不是有类似 toString 的函数?

子类化 Javascript 数组。 TypeError:Array.prototype.toString 不是通用的

如何判断Javascript函数是否是Async函数