¿Es posible anular la función toString () de JavaScript para proporcionar una salida significativa para la depuración?

115

Cuando console.log()visualizo un objeto en mi programa JavaScript, solo veo la salida [object Object], que no es muy útil para averiguar qué objeto (o incluso qué tipo de objeto) es.

En C # estoy acostumbrado a anular ToString()para poder personalizar la representación del depurador de un objeto. ¿Hay algo similar que pueda hacer en JavaScript?

devios1
fuente
2
Encuentro que la salida es la forma más confiable de decirle qué contiene una variable (o al menos mejor que typeof).
alex

Respuestas:

102

También puede anular toStringen Javascript. Ver ejemplo:

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]

Vea esta discusión sobre cómo determinar el nombre del tipo de objeto en JavaScript.

Michael Spector
fuente
8
Si bien es cierto, la función de alerta mostrará el valor de retorno de la función que anula la toStringpropiedad del prototipo , Object.prototype.toString.call(f)seguirá mostrándose [object Object].
Frederik Krautwald
14
'Object.prototype.toString.call (f) seguirá mostrando [object Object].' Sí, porque esa es una función completamente diferente a 'Foo.prototype.toString', lol.
Triynko
5
En caso de que alguien como yo termine aquí, puede usar Sybmol.toStringTag en ES6 para personalizar el comportamiento de Object.prototype.toString.call. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
TLadd
32

Primero anule toStringsu objeto o el prototipo:

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

var foo = new Foo();

Luego, convierta a cadena para ver la representación de cadena del objeto:

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

Si no le gusta la escritura adicional, puede crear una función que registre representaciones de cadena de sus argumentos en la consola:

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

Uso:

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

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

Actualizar

E2015 proporciona una sintaxis mucho mejor para estas cosas, pero tendrás que usar un transpilador como 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'
Max Heiber
fuente
5
console.log ('' + foo); Este fue el problema. No vi ninguna implementación de toString hasta que llegué a su respuesta.
ahmadalibaloch
13

Una forma fácil de obtener resultados depurables en el navegador JS es simplemente serializar el objeto en JSON. Entonces podrías hacer una llamada como

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

Por ejemplo, alert("Blah! " + JSON.stringify({key: "value"}));produce una alerta con el textoBlah! {"key":"value"}

Pablo V
fuente
Eso es muy útil. La salida puede ser un poco enorme, imagino, ¡pero funciona en caso de apuro!
devios1
@dev Handy, pero no anula toString ().
Dan Dascalescu
10

Si está utilizando Node, podría valer la pena considerarlo 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 );

Esto producirá:

{ #Point 1,2 }

Mientras que la versión sin inspeccionar imprime:

{ x: 1, y: 2 }
SistemáticoFrank
fuente
6

Simplemente anule el toString()método.

Ejemplo simple:

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

Lo hace aún mejor cuando define un nuevo tipo:

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}"
Matt Ball
fuente
9
Este código no resuelve el problema console.log del OP, al menos no en node.js v0.10.*o Chrome Version 32.0.1700.102. Mientras que llamar a toString directamente (lame) o usar coerción de tipo (lamer) funcionará con esto, la consola [/ info | log /] usa el antiguo pre-mod toString.
james_womack
1
Es 2019 ahora y tanto los objetos de impresión bonita de nodejs como de Chrome por sí solos, por lo que la coerción (cuando agrega el objeto a una cadena) es el único caso de uso en el que debería buscar en Google esta pregunta, creo.
Klesun
6

Si el objeto lo define usted mismo, siempre puede agregar una anulación de 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);

Niño pirateado
fuente
5

Con literales de plantilla :

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

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

Agregue la propiedad 'Symbol.toStringTag' al objeto o clase personalizados.

El valor de cadena que se le asigna será su descripción de cadena predeterminada porque se accede internamente por el Object.prototype.toString() método .

Por ejemplo:

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

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

Algunos tipos de JavaScript, como Maps y Promises, tienen un toStringTagsímbolo integrado definido

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

Dado que Symbol.toStringTages un símbolo conocido , podemos hacer referencia a él y verificar que los tipos anteriores tengan la propiedad Symbol.toStringTag -

new Map()[Symbol.toStringTag] // 'Map'
Promise.resolve()[Symbol.toStringTag] // 'Promise'
Danield
fuente
¿Es esto junto con anular toString()directamente la única forma de lograrlo function MyObj() {} Object.prototype.toString.call(new MyObj()) // "[object MyObj]"?
tonix
1
@tonix - Creo que sí ... Si hay otra forma, hágamelo saber;)
Danield
0

El registro de la consola de Chrome le permite inspeccionar el objeto.

tomconte
fuente
Sí, eso es cierto si solo saco el objeto, lo cual es útil. Sin embargo, a veces solo quiero generarlo como parte de una cadena que podría usar para contener otros datos y sería bueno si pudiera personalizar ese formulario de alguna manera.
devios1
6
Acabo de descubrir que se puede utilizar argumentos adicionales en un console.log a la salida de objetos en línea con una cadena: console.log("this is my object:", obj).
devios1
0

-Esta operación requiere mucho tiempo para completarse, y su uso se desaconseja según los documentos de mozilla: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/proto

-Aparentemente, los navegadores modernos desaprobaron .prototype y ECMA6 especifica el uso apropiado__proto__ en su lugar.

Entonces, por ejemplo, si está definiendo su propia geoposición de objetos , debe llamar a la propiedad __proto__ en lugar de .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());
jmojico
fuente
0

A continuación, se muestra un ejemplo de cómo secuenciar un objeto Map:

  Map.prototype.toString = function() {

    let result = {};

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

    return JSON.stringify(result);
  };
Agustí Sánchez
fuente
-1

Puede dar a cualquier objeto personalizado sus propios métodos toString, o escribir uno general al que pueda llamar en el objeto que está mirando.

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);
}
Kennebec
fuente
-1

En lugar de anular toString(), si incluye la biblioteca de prototipos de JavaScript , puede utilizarla Object.inspect()para obtener una representación mucho más útil.

Los frameworks más populares incluyen algo similar.

Codelahoma
fuente
-1

Puede extender o anular en JS

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

ch2o
fuente
¿Cómo agrega esto algo a las respuestas de 2011 que dan la misma solución?
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;
};
aricca
fuente