Is it possible to override JavaScript's toString() function to provide meaningful output for debugging?
JavascriptDebuggingTostringJavascript Problem Overview
When I console.log()
an object in my JavaScript program, I just see the output [object Object]
, which is not very helpful in figuring out what object (or even what type of object) it is.
In C# I'm used to overriding ToString()
to be able to customize the debugger representation of an object. Is there anything similar I can do in JavaScript?
Javascript Solutions
Solution 1 - Javascript
You can override toString
in Javascript as well. See example:
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]
See this discussion on how to determine object type name in JavaScript.
Solution 2 - Javascript
First override toString
for your object or the prototype:
var Foo = function(){};
Foo.prototype.toString = function(){return 'Pity the Foo';};
var foo = new Foo();
Then convert to string to see the string representation of the object:
//using JS implicit type conversion
console.log('' + foo);
If you don't like the extra typing, you can create a function that logs string representations of its arguments to the console:
var puts = function(){
var strings = Array.prototype.map.call(arguments, function(obj){
return '' + obj;
});
console.log.apply(console, strings);
};
Usage:
puts(foo) //logs 'Pity the Foo'
puts(foo, [1,2,3], {a: 2}) //logs 'Pity the Foo 1,2,3 [object Object]'
Update
E2015 provides much nicer syntax for this stuff, but you'll have to use a transpiler like 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'
Solution 3 - Javascript
If you are using Node it might be worth considering 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 );
This will yield:
{ #Point 1,2 }
While the version without inspect prints:
{ x: 1, y: 2 }
More information (+ an example for using in class
es):
Solution 4 - Javascript
An easy way to get debuggable output in browser JS is to just serialize the object to JSON. So you could make a call like
console.log ("Blah: " + JSON.stringify(object));
So for an example, alert("Blah! " + JSON.stringify({key: "value"}));
produces an alert with the text Blah! {"key":"value"}
Solution 5 - Javascript
With template literals:
class Foo {
toString() {
return 'I am foo';
}
}
const foo = new Foo();
console.log(`${foo}`); // 'I am foo'
Solution 6 - Javascript
Add the 'Symbol.toStringTag' property to the custom object or class.
The string value which is assigned to it will be its default string description because it is accessed internally by the Object.prototype.toString()
method.
For example:
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));
Some Javascript types such as Maps and Promises have a built-in toStringTag
symbol defined
Object.prototype.toString.call(new Map()); // "[object Map]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
Because Symbol.toStringTag
is a well-known symbol, we can reference it and verify that the above types do have the Symbol.toStringTag property -
new Map()[Symbol.toStringTag] // 'Map'
Promise.resolve()[Symbol.toStringTag] // 'Promise'
Solution 7 - Javascript
Just override the toString()
method.
Simple example:
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
It does even better when you define a new type:
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}"
Solution 8 - Javascript
If the object is defined by yourself you can always add a toString override.
//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);
Solution 9 - Javascript
You can give any custom objects their own toString methods, or write a general one that you can call on the object you are looking at-
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);
}
Solution 10 - Javascript
-This operation takes lot of time to complete, and it's use is discouraged according to mozilla docs: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/proto
-Apparently, modern browsers deprecated .prototype and ECMA6 specifies using proper__proto__ instead.
So for example, if you are defining you own object geoposition you should call proto property instead of .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());
Solution 11 - Javascript
Here's an example how to stringify a Map object:
Map.prototype.toString = function() {
let result = {};
this.forEach((key, value) => { result[key] = value;});
return JSON.stringify(result);
};
Solution 12 - Javascript
The Chrome console log allows you to inspect the object.
Solution 13 - Javascript
Rather than overriding toString()
, if you include the Prototype JavaScript Library, you can use Object.inspect()
to get a much more useful representation.
Most popular frameworks include something similar.
Solution 14 - Javascript
You can extend or override in JS
String.prototype.toString = function() {
return this + "..."
}
document.write("Sergio".toString());
Solution 15 - Javascript
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;
};