有没有JavaScript的__repr__等价物?

46

我接近于Python中的repr的东西是这个:

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

有没有一种默认的方法可以将一个对象表示为字符串?或者我只能使用object.variable来获得想要的结果?


1
console.log(user+"") - epascarello
1
console.log(String(user)) - epascarello
由于 toString 函数不包括 password 的值,它怎么类似于一个 __repr__ 实现呢? - Chris Martin
2
你可以编写自己的 repr 函数,以查找 __repr__ 方法。 - Eevee
好的,就像Eevee所说的那样,你可以通过添加window.repr = function (x) { return x.__repr__(); };来复制Python的行为。虽然不确定为什么这样做,但这并不是Python一个值得称赞的设计决策。 - Chris Martin
显示剩余3条评论
9个回答

33

JSON.stringify 可能是最接近本地库的方法。它不能很好地处理对象,但您可以定义自己的代码来解决这个问题。我搜索了提供此功能的库,但没有找到任何内容。


JSON.stringify("Hello, world!") is still "Hello, world!" - RixTheTyrunt

24

Node.js util.inspect

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

要获取在终端上显示为字符串的对象表示形式,可以执行以下操作:

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"});

输出:

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

如果你将字符串 { a: "0\n1", b: "c"} 复制粘贴到终端并按下回车键,它将显示与此相同的内容。

覆盖类的inspect方法以自定义表示方式

这个问题被问及:如何更改Nodejs调试控制台视图中对象的字符串表示形式,并在https://dev59.com/t2Af5IYBdhLWcg3wOQYL#32866283https://dev59.com/t2Af5IYBdhLWcg3wOQYL#54667225中提到,但这里有一个更详细的示例:

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);

输出:

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

如果我们没有定义自定义的[util.inspect.custom],默认的检查将如下所示:

MyClass { a: 1, b: 2 }

同样的表达也可以在终端上直接检查对象时显示:

> my_object
a is 1 and b is 2
在旧版的Node.js中,定义自定义打印机的语法只需写成:

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

但是这已经被弃用并伴随着以下信息:

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

如下所述:

使用换行和缩进漂亮地打印util.inspect

不可能吗?叹息:

相比JSON.stringify,一个很大的优势是inspect会为您处理循环依赖项。但没有漂亮的打印,这很麻烦。

在Node.js v10.15.1中测试。


你需要 var util = require('util'); - ctrl-alt-delor
@richard 感谢您的建议。由于某些我不理解的原因,在我当时测试的 node 0.10.26 上,它在没有 require 的情况下就可以工作了,但我也不确定那时候这样做是否是个好主意。 - Ciro Santilli OurBigBook.com
1
看起来 inspectJSON.stringify 更详细,因为它包括构造函数名称。 - dpercy
1
这可能是答案吗? - nlhnt

13
在 Nodejs 中,可以通过向 JavaScript 对象添加 inspect() 方法来覆盖 console.log 的对象表示形式。
例如:
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 ; }

-感谢'Ciro Santilli'


我认为它不再有效了,至少在Chrome版本79.0.3945.117上不行。 - Andrey
我在谈论Nodejs。 - harish2704
1
没有任何地方指明。 - Andrey
2
请注意,覆盖 inspect 已被弃用,现在您必须实现 [util.inspect.custom]:https://dev59.com/t2Af5IYBdhLWcg3wOQYL#26698403 - Ciro Santilli OurBigBook.com

2

对我来说,一个快速的方法是使用数组字面量将值包装起来,像这样:

console.log([variable]);

在浏览器的开发者控制台中,输出内容非常清晰地显示了数组中唯一元素是什么。

Firefox上开发者控制台的截图


2

是的,这完全可以用JavaScript实现!

您可以直接跳到结尾查看解决方案,或者阅读以下详细说明。

解决方法

解决方案取决于您如何实现它,因为运行时环境会发挥作用。浏览器实现运行时环境,强制执行它们采用的ECMAScript标准,而Node.js是V8的分支,具有处理标准的自己的模块。

下面,我将提供一个用例并给出两个解决方案以及问题的直接解决方案:

  1. 通用解决方案,您可以在浏览器和Node.js上实现并使用
  2. Node.js特定解决方案。
  3. OP的确切用例的解决方案

注意:这也将帮助那些正在学习数据结构并尝试进行JavaScript实现的人。


问题

让我们以与OP的用例类似的用例为例,以更好地理解这一点。假设您正在从头开始构建数组数据结构作为自定义xArray构造函数。为简单起见,我们只实现push方法,以便我们可以推入一些数据,然后尝试以JavaScript数组构造函数创建对象并打印数组的方式输出结果,当我们console.log对象或从函数返回它时。

我们还需要静态方法XArray.isArray(input),以便我们可以测试对象实例是否与构造函数相同。

如果我们简单地将对象console.log而不添加解决方案,则会得到类似以下内容的内容:

不良解决方案:

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' }

但这不是我们想要的。我们需要像OP的用例中一样使用Object.values(xArray),输出需要user.name而不是整个User { name: 'example', password: 'password' }

因此,这是我们想要的输出:

期望的解决方案:

// 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

让我们先构建数组的解决方案,然后简单地将其实现到OP的问题上。


解决方案

解决方案1 - 通用解决方案适用于所有运行时

我们将通过修改具有log方法的全局对象来修改构造函数内部的console.log()

P.S. - 您可以使用console.log(console)查看所有控制台方法。

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;
  }
}

解决方案2: 使用util模块进行Node.js翻译

我们将使用node.js中的util模块中的util.inspect.custom符号。

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);
  }
}

解决方案3:解决用户使用情况

您可以使用解决方案1或解决方案2来解决您的使用情况:

解决方案3A:通用解决方案使用解决方案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;
  }
}

解决方案3B:使用解决方案2的Node.js

import util from 'util';

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

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

测试解决方案3A和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

旁注:

  • import 是 ES6 的一种约定,如果您没有启用 "type": "module" 设置,则默认情况下在 Node.js 中无法使用。这是因为 Node 默认遵循 CommonJS 约定。 如果这让您感到困惑,请替换导入行并使用:const util = require('util');

2
String(user)

我认为这是最好的选择。另一个选择是寻找第三方库,用于处理对象的人类可读形式展示。


你知道有什么好的吗?但我想这样也可以。 - user3234209
我脑海中没有。原型路线也不错的想法。 - exit2

2
Node v6.6.0 引入了 util.inspect.custom 符号:它是一个全局注册的符号,可以通过 Symbol.for('nodejs.util.inspect.custom') 访问。它可以用于声明自定义检查函数。
以下是 OP 案例的使用示例:
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'

0

0
正如安德鲁·约翰逊所说,JSON.stringify 可能是你可以直接使用的最接近的方法。
一种常见的 repr 策略是 输出可运行的 Python 代码。如果你想这样做,lave(与 eval 相反)是一个不错的选择。
示例:
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}));

输出(不如我所希望的那么优雅!):

var a = Object.create({ 'constructor': function User(name, password){
             this.name = name;
             this.password = password;
} });
a.name = 'example';
a.password = 'password';
a;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接