【问题标题】:Prototypal inheritance in JavaScript with spread operatorJavaScript 中的原型继承与扩展运算符
【发布时间】:2020-10-28 21:10:14
【问题描述】:

经过长时间的冲突,我正在尝试修复我与 JavaScript 的关系:D

在以下代码中:

class e extends Error{
    constructor(message,id){
        super(message)
        this.id = id
    }
}

const e1 = new e('message',12);
const e2 = {...e1}

console.log(e2);
}

结果是{id:12}

如您所见,message 属性不存在,我理解这是因为 message 属性不属于 e2。

我的问题是最好的方法是什么使得结果是{id:12,message:'message'}

我知道这可能是一个非常新手的问题,但我感谢您对此的详细说明

用例

我在下面有一个全局错误处理程序,如果在生产模式下,我需要将 err 对象复制到错误对象中,但我发现我需要手动添加这一行 error.message = err.message;

我想知道是否可以在不手动添加此行的情况下克隆 err 对象,即在不知道其超类属性的情况下克隆对象的最佳方法是什么?

module.exports = (err, req, res, next) => {

  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';
  console.log(process.env.NODE_ENV)

  if (process.env.NODE_ENV === 'development') {
    sendErrorDev(err, res);
  } else if (process.env.NODE_ENV === 'production') {
    let error = { ...err };
    error.message = err.message;
   // console.log(err)

    //   if (error.name === 'CastError') error = handleCastErrorDB(error);
    if (error.code === 11000) error = handleDuplicateFieldsDB(error);
    //   if (error.name === 'ValidationError')
    //     error = handleValidationErrorDB(error);

    sendErrorProd(error, res);
  }
};

【问题讨论】:

  • 也许this.message 不可枚举? Object.keys(e1) // ['id'] 让我觉得不是。所以传播运算符会错过它。
  • @evolutionxbox 是的,我知道 e2.hasOwnProperty('id') -> true 但是 e2.hasOwnProperty('message') -> false,我想知道如果可能的话如何进行传播操作员工作
  • 消息需要成为可枚举的属性。这可能意味着您不能从 Error 继承。
  • 老实说,如果我知道怎么写,我会写一个答案。我知道Object.defineProperty 会有所帮助。 ——如果你不能使用它定义的属性,继承自 Error 的原因是什么?或者也许使用传播作为一种继承方式并不好?
  • 从您列出的内容来看,您需要克隆它并不明显。最终重要的是错误消息。对于任意错误,您可能希望根据之前的错误new Error(oldErr.message) 构建它。您可以将原始错误存储在 errors 字段中,以备日后需要时参考 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

标签: javascript node.js inheritance


【解决方案1】:

正如 cmets 中所确定的,这与继承关系不大,而与message not being enumerable 关系更大。它仍然是每个实例自己的属性。

您可以在自定义子类中更改它,但可以使用 Object.defineProperty:

class E extends Error{
    constructor(message,id){
        super(message)
        Object.defineProperty(this, 'message', {enumerable: true})
        this.id = id
    }
}
const e = new E('message', 12);
console.log({...e});

这将导致对象文字中的扩展语法复制属性。但是,如果您的目标是获得具有相同自定义原型的新实例,则不能使用扩展语法。改写一个方法:

class E extends Error{
    constructor(message,id){
        super(message)
        this.id = id
    }
    clone() {
        return new E(this.message, this.id)
    }
}
const e = new E('message', 12);
console.log(e.clone());

【讨论】:

  • 好的,这太棒了,我想这几乎就是我想要的,谢谢,但是为什么在我的代码中 console.log(e2.hasOwnProperty('message')) 给出 false 如果你说“它仍然是每个人自己的财产实例。”?
  • @SaedNabil 因为e2 是您创建的没有属性的对象文字,而不是e 实例。 e1.hasOwnProperty('message') 将是 true。
  • 它仍然是每个实例自己的属性-用您的话来说,每个实例是什么意思?您的意思是您不将 e2 视为实例?这是你的意思吗?
  • 实例是从相应原型继承的对象(和/或由相应的构造函数构造)。短语“每个实例”的意思是“e(或Error)的每个实例”。 e1 instanceof e 和 Object.getPrototypeOf(e1) == e.prototype,但 e2 充其量是 Object 实例。它不是 e 或 Error 实例。
【解决方案2】:

设置原型的最佳方法是不要使用对象传播来执行此操作,因为对象字面量会生成一个继承自 Object 的普通对象。

解决这个问题的方法是:

Object.setPrototypeOf(e2, e)

更正确的方法是创建一个具有适当原型的对象:

e2 = Object.create(e1)

要在分配时隐藏继承的属性,可以将其与 Object.assign 组合以将它们作为普通对象提供。

这些都不会产生{id:12,message:'message'} 输出。 message 是在 Error.prototype 上声明的不可枚举属性,Error.prototype.toString 使用它来字符串化错误。

如果意图不使用原型继承而是生成{id:12,message:'message'} 普通对象,则需要:

e2 = {...e1, message: e1.message}
猜你喜欢
  • 2016-07-29
  • 2013-11-07
  • 2015-11-13
  • 1970-01-01
  • 2010-09-28
  • 2010-09-28
  • 2018-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多