45

The closest I got to something close to Python's repr is this:

function User(name, password){
         this.name = name;
         this.password = password;
}
User.prototype.toString = function(){
    return this.name;
};



var user = new User('example', 'password');

console.log(user.toString()) // but user.name would be even shorter

Is there a way to represent an object as a string by default? Or am I going to have to just use object.variable to get the results I want?

user3234209
  • 935
  • 2
  • 8
  • 14
  • 1
    `console.log(user+"")` – epascarello Jul 23 '14 at 04:51
  • 1
    `console.log(String(user))` – epascarello Jul 23 '14 at 04:54
  • @Epascarello Those both work, so if no one else answers and those are the only two ways of doing it I suggest you post those as an answer so you can get credit. I hope their is an alternative. – user3234209 Jul 23 '14 at 04:55
  • Are you asking specifically about an equivalent to `dict.__repr__`? – Chris Martin Jul 23 '14 at 05:16
  • Since that `toString` function doesn't include the value of `password`, how is it akin to a `__repr__` implementation? – Chris Martin Jul 23 '14 at 05:17
  • 2
    you can always write your own `repr` function that looks for `__repr__` methods – Eevee Jul 23 '14 at 05:23
  • Eevee, Good point... but I was hoping JavaScript had a built in one. Chris Martin, No, I only want the representation to return the name the password really doesn't need to be returned and about the dic.__repr__ I'm not entirely sure what you mean. I was just talking about the __repr__ function you can add under a class so when you call it it returns whatever you wanted under it. – user3234209 Jul 23 '14 at 05:28
  • Well then, yeah, as Eevee said, you can reproduce the behavior of Python just by adding `window.repr = function (x) { return x.__repr__(); };`. Not sure why, though. It's not exactly an admirable design decision by Python. – Chris Martin Jul 23 '14 at 06:03

9 Answers9

33

JSON.stringify is probably the closest you are going to get from native libraries. It doesn't work well with objects, but you could define your own code to work around that. I searched for libraries that provide this functionality but didn't find anything.

Andrew Johnson
  • 3,078
  • 1
  • 18
  • 24
23

Node.js util.inspect

http://nodejs.org/api/util.html#util_util_inspect_object_options

To get the object representation that appears on the terminal as a string you can do:

const util = require('util');
console.log(util.inspect({ a: "0\n1", b: "c"}));
// Equivalent: automatically called by `console.log`.
console.log({ a: "0\n1", b: "c"});

output:

{ a: '0\n1', b: 'c' }
{ a: '0\n1', b: 'c' }

This is the same that would appear on the terminal if you copy pasted the string { a: "0\n1", b: "c"} into the terminal and pressed enter.

Override the inspect method of a class for a custom representation

This was asked at: How to change string representation of objects in Nodejs debug console view and mentioned at: https://stackoverflow.com/a/32866283/895245 and https://stackoverflow.com/a/54667225/895245 but here goes a fuller example:

const util = require('util');

class MyClass {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
  [util.inspect.custom]() {
    return `a is ${this.a} and b is ${this.b}`;
  }
}

const my_object = new MyClass(1, 2);
console.log(util.inspect(my_object));
console.log(my_object);

Output:

a is 1 and b is 2
a is 1 and b is 2

The default inspect if we hadn't defined a custom [util.inspect.custom] would have been:

MyClass { a: 1, b: 2 }

That same representation also shows if you just directly inspect the object on a terminal:

> my_object
a is 1 and b is 2

In older Node.js, the syntax to define the custom printer used to be just:

inspect() {
  return `a is ${this.a} and b is ${this.b}`;
}

but that was deprecated with message:

[DEP0079] DeprecationWarning: Custom inspection function on Objects via .inspect() is deprecated

as mentioned at:

Pretty print util.inspect with newlines and indentation

Not possible? Sigh:

One big advantage over JSON.stringify is that inspect takes care of circular dependencies for you. But without pretty print, it is a pain.

Tested in Node.js v10.15.1.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
13

In Nodejs, console.log representation of a Javascript object can be overridn by adding inspect() method to that Object

eg:

function User(name, password){
         this.name = name;
         this.password = password;
}
User.prototype.toString = function(){
    return this.name;
};
User.prototype.inspect = function(){ return 'Model: ' + this.name ; }

-Thanks to 'Ciro Santilli'

harish2704
  • 591
  • 6
  • 16
2
String(user)

Is the best I can think of. I think another alternative may be to find a 3rd party lib that handles creating human readable presentation for objects.

exit2
  • 31
  • 2
2

A quick shortcut for me is to wrap the value with an array literal, like this:

console.log([variable]);

The output in the browser's developer console makes it quite clear what the only element of the array is.

Screenshot of developer console on Firefox

Flimm
  • 136,138
  • 45
  • 251
  • 267
2

Node v6.6.0 introduced the util.inspect.custom symbol: it is a globally registered symbol accessible through Symbol.for('nodejs.util.inspect.custom'). It can be used to declare custom inspect functions.

Here's a usage example with OP's case:

function User(name, password){
  this.name = name;
  this.password = password;
  this[Symbol.for('nodejs.util.inspect.custom')] = () => this.name;
}

var user = new User('example', 'password');

console.log(user)  // 'example'
cloris
  • 46
  • 5
1

Yes, this is 100% possible with JavaScript!

You can Skip to the end for the solution directly or read this for a detailed explanation.

The Approach

The solution depends on how you want to implement it as the runtime environment comes into play. Browsers implement runtime environments that enforce ECMAScript standards they adopt while Node.js is a fork on V8 and has it's own modules that deals with how standard

Below, I will take a use case and give two solutions and direct solution to the example problem of your question:

  1. An Universal Solution that you can implement and it will work on the browser AND on Node.js
  2. Node.js specific solution.
  3. Solution to the exact Use Case of OP.

Note: This will also help those studying Data Structures and trying to do a JavaScript implementation.


The Problem

Let's take a Use Case similar to the OP's use case to understand this better. Suppose you are building the Array Data Structure from scratch as a custom xArray constructor. For simplicity let's only implement the push method so we can push some data and then try to output the result exactly the way the JavaScript Array constructor creates objects and the prints the array when we console.log the object or return it from a function.

We will also need the static method XArray.isArray(input) so we can test if the instance of the object is the same as the constructor.

If we simply console.log the Object without adding the solution, we will get something that looks like:

Undesired Solution:

const xArray = new XArray();
xArray.push('a', 'b', 'c', 'd', 'e');
console.log(xArray);
// Prints: XArray { '0': 'a', '1': 'b', '2': 'c', '3': 'd', '4': 'e' }

But that's not what we want. We need the Object.values(xArray) just like in the OP's use case, user.name is required as the output and not the whole User { name: 'example', password: 'password' }

So, instead, this is the output we want:

Desired Solution:

// Test function that takes an array and returns it.
const returnArr = array => array;

/************************************************************************************************/
/** Array using custom XArray constructor **/
/************************************************************************************************/
const xArray = new XArray();
xArray.push('a', 'b', 'c', 'd', 'e');
console.log(xArray); // Prints: [ 'a', 'b', 'c', 'd', 'e' ]
console.log(returnArr(xArray)); // Returns: [ 'a', 'b', 'c', 'd', 'e' ]
console.log(XArray.isArray(xArray)); // Returns: true

Let's build the solution for the array and then simply implement that for the OP's question.


The Solution

Solution 1 - Universal Solution for all Runtimes

We will modify the console.log() inside the constructor function by modifying global object that has the log method.

P.S. - You can console.log(console) to see all the console methods.

class XArray {
  constructor() {
    Object.defineProperties(this, {
      length: {
        writable: true,
        enumerable: false,
        configurable: false,
        value: 0,
      },
    });

    const runtimeConsole = console;
    console = {
      ...console,
      log: function (data) {
        if (XArray.isArray(data)) runtimeConsole.log(Object.values(data));
        else runtimeConsole.log(data);
      },
    };
  }

  push(...elements) {
    for (const element of elements) {
      this[this.length] = element;
      this.length++;
    }
  }

  static isArray(array) {
    return array instanceof XArray;
  }
}

Solution 2: For Node.js using util module

We will be using the util.inspect.custom Symbol from the util module in node.js.

import util from 'util';

class XArray {
  constructor() {
    Object.defineProperties(this, {
      length: {
        writable: true,
        enumerable: false,
        configurable: false,
        value: 0,
      },
    });
  }

  push(...elements) {
    for (const element of elements) {
      this[this.length] = element;
      this.length++;
    }
  }

  [util.inspect.custom]() {
    return Object.values(this);
  }
}

Solution 3: Solving the OP's Use Case

So you can use either Solution 1 or Solution 2 to solve your Use Case:

Solution 3A: Universal Solution Using Solution 1

class User {
  constructor(name, password) {
    this.name = name;
    this.password = password;

    const runtimeConsole = console;
    console = {
      ...console,
      log: function (data) {
        if (User.isUser(data)) runtimeConsole.log(data.name);
        else runtimeConsole.log(data);
      },
    };
  }

  static isUser(user) {
    return user instanceof User;
  }
}

Solution 3B: For Node.js Using Solution 2

import util from 'util';

function User(name, password) {
  this.name = name;
  this.password = password;

  [util.inspect.custom]() {
    return this.name;
  }
}

Testing Solution 3A and 3B:

// Test function that takes an user and returns it
const user = new User('example', 'password');

// Function that takes an user and returns it
const returnUser = user => user;

console.log(user); // Prints: example
console.log(returnUser(user)); // Prints: example

Sidenote:

  • import is an ES6 convention that will not work in Node.js by default unless you have the package.json file have a "type": "module" setting enabled. This is because Node by default honours the CommonJS convention. If this is confusing, replace the import line and use: const util = require('util');
0

As Andrew Johnson said, JSON.stringify is probably the closest you can get out of the box.

One common strategy for a repr is to output runnable Python code. If you want to do this, lave (opposite of eval) is a good choice.

Example:

var escodegen = require('escodegen')
var lave = require('lave')

function User(name, password){
             this.name = name;
             this.password = password;
}

var user = new User('example', 'password');

console.log(lave(user, {generate: escodegen.generate}));

Output (not as elegant as I had hoped!):

var a = Object.create({ 'constructor': function User(name, password){
             this.name = name;
             this.password = password;
} });
a.name = 'example';
a.password = 'password';
a;
Community
  • 1
  • 1
paulkernfeld
  • 2,171
  • 1
  • 16
  • 16
0

This is solution for NodeJS (not sure about browser). As https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_inspect_object_options says, you could add inspect(depth, opts) to your class and it will be called when you console.log(user_class_instance);

Therefore this should do the trick:

User.prototype.inspect = function(depth, opts){
    return this.name;
};
Alex Velickiy
  • 541
  • 1
  • 7
  • 22