Czy można przesłonić funkcję toString () JavaScript, aby zapewnić znaczące dane wyjściowe do debugowania?

115

Kiedy console.log()w moim programie JavaScript widzę obiekt, po prostu widzę dane wyjściowe [object Object], które nie są zbyt pomocne w ustaleniu, jaki to obiekt (ani nawet jakiego typu).

W C # jestem przyzwyczajony do zastępowania, ToString()aby móc dostosować reprezentację debugera do obiektu. Czy jest coś podobnego, co mogę zrobić w JavaScript?

devios1
źródło
2
Uważam, że dane wyjściowe są najbardziej niezawodnym sposobem określenia, co zawiera zmienna (lub przynajmniej lepiej niż typeof).
Alex

Odpowiedzi:

103

Możesz również nadpisać toStringw JavaScript. Zobacz przykład:

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]

Zobacz dyskusję, jak określić nazwę typu obiektu w JavaScript.

Michael Spector
źródło
8
Chociaż prawdą jest, że funkcja alertu wyświetli wartość zwracaną przez funkcję przesłaniającą toStringwłaściwość prototypu , Object.prototype.toString.call(f)nadal będzie wyświetlana [object Object].
Frederik Krautwald
14
„Object.prototype.toString.call (f) nadal będzie wyświetlać [obiekt Object]”. Tak, ponieważ to zupełnie inna funkcja niż „Foo.prototype.toString”, lol.
Triynko
5
Na wypadek, gdyby ktoś inny jak ja znalazł się tutaj, możesz użyć Sybmol.toStringTag w ES6, aby dostosować zachowanie Object.prototype.toString.call. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
TLadd
32

Najpierw nadpisanie toStringdla twojego obiektu lub prototypu:

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

var foo = new Foo();

Następnie przekonwertuj na ciąg, aby zobaczyć reprezentację ciągu obiektu:

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

Jeśli nie podoba ci się dodatkowe wpisywanie, możesz utworzyć funkcję, która rejestruje łańcuchowe reprezentacje swoich argumentów w konsoli:

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

Stosowanie:

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

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

Aktualizacja

E2015 zapewnia o wiele ładniejszą składnię dla tych rzeczy, ale będziesz musiał użyć transpilera, takiego jak 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
źródło
6
console.log ('' + foo); to był problem. Nie widziałem żadnej implementacji toString, dopóki nie dotarłem do twojej odpowiedzi.
ahmadalibaloch
13

Prostym sposobem uzyskania danych wyjściowych, które można debugować w przeglądarce JS, jest po prostu serializacja obiektu do formatu JSON. Więc możesz zadzwonić w stylu

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

Na przykład alert("Blah! " + JSON.stringify({key: "value"}));generuje alert z tekstemBlah! {"key":"value"}

Paweł V.
źródło
To jest bardzo przydatne. Wyobrażam sobie, że wydajność może być nieco ogromna, ale działa w mgnieniu oka!
devios1
@dev Handy, ale nie zastępuje metody toString ().
Dan Dascalescu
10

Jeśli używasz Node, warto to rozważyć 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 );

To da:

{ #Point 1,2 }

Natomiast wersja bez kontroli wydruków:

{ x: 1, y: 2 }
SystematicFrank
źródło
6

Po prostu zastąp toString()metodę.

Prosty przykład:

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

Działa jeszcze lepiej, gdy zdefiniujesz nowy typ:

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
źródło
9
Ten kod nie rozwiązuje problemu console.log OP, przynajmniej nie w node.js v0.10.*lub Chrome Version 32.0.1700.102. Podczas bezpośredniego wywoływania toString (lame) lub używania koercji typu (lamer) będzie z tym działać, console [/ info | log /] używa starego przedmodelowania toString.
james_womack
1
Teraz jest rok 2019 i zarówno nodejs, jak i chrome ładne obiekty drukowane same, więc przymus (kiedy dodajesz obiekt do ciągu) jest jedynym przypadkiem użycia, w którym możesz znaleźć w Google to pytanie.
Klesun
6

Jeśli obiekt jest zdefiniowany przez Ciebie, zawsze możesz dodać przesłonięcie 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);

Zhakowane dziecko
źródło
3

Dodaj właściwość „Symbol.toStringTag” do obiektu lub klasy niestandardowej.

Wartość ciągu, która jest do niego przypisana, będzie domyślnym opisem ciągu, ponieważ Object.prototype.toString()metoda jest dostępna wewnętrznie .

Na przykład:

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

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

Niektóre typy JavaScript, takie jak Mapy i Obietnice, mają toStringTagzdefiniowany wbudowany symbol

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

Ponieważ Symbol.toStringTagjest to dobrze znany symbol , możemy się do niego odwołać i sprawdzić, czy powyższe typy mają właściwość Symbol.toStringTag -

new Map()[Symbol.toStringTag] // 'Map'
Promise.resolve()[Symbol.toStringTag] // 'Promise'
Danield
źródło
Czy jest to toString()jedyny sposób na osiągnięcie tego w połączeniu z bezpośrednim nadpisywaniem function MyObj() {} Object.prototype.toString.call(new MyObj()) // "[object MyObj]"?
tonix
1
@tonix - Tak mi się wydaje ... Jeśli jest inny sposób, daj mi znać;)
Danield
0

Dziennik konsoli Chrome umożliwia sprawdzenie obiektu.

tomconte
źródło
Tak, to prawda, jeśli wyświetlam tylko obiekt, co jest przydatne. Czasami jednak chcę wyprowadzić go jako część ciągu, którego mógłbym użyć do zawarcia innych danych i byłoby miło, gdybym mógł dostosować ten formularz w jakiś sposób.
devios1
6
Właśnie odkryłem, że można korzystać z dodatkowych argumentów w console.log do wyświetlania obiektów inline z ciągiem: console.log("this is my object:", obj).
devios1
0

-Ta operacja zajmuje dużo czasu i jej użycie jest odradzane zgodnie z dokumentacją Mozilli: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/proto

-Wydaje się, że nowoczesne przeglądarki są przestarzałe .prototype, a ECMA6 używa zamiast tego właściwego _proto__.

Tak na przykład, jeśli jesteś właścicielem obiektu definiowania geoposition należy zadzwonić __proto__ własność zamiast .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
źródło
0

Oto przykład, jak zdefiniować obiekt Map:

  Map.prototype.toString = function() {

    let result = {};

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

    return JSON.stringify(result);
  };
Agustí Sánchez
źródło
-1

Możesz nadać dowolnym obiektom niestandardowym własne metody toString lub napisać ogólną metodę, którą możesz wywołać dla obiektu, na który patrzysz -

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
źródło
-1

Zamiast nadpisywać toString(), jeśli włączysz bibliotekę Prototype JavaScript , możesz użyć jej, Object.inspect()aby uzyskać znacznie bardziej użyteczną reprezentację.

Najpopularniejsze frameworki zawierają coś podobnego.

codelahoma
źródło
-1

Możesz rozszerzyć lub zastąpić w JS

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

ch2o
źródło
W jaki sposób to dodaje cokolwiek do odpowiedzi z 2011 roku, które dają to samo rozwiązanie?
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
źródło