简体   繁体   中英

Prototypal inheritance in JavaScript with spread operator

I am trying to repair my relationship with JavaScript after a long time of conflict :D

In the following code :

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

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

console.log(e2);
}

the result is {id:12}

As you can see the message property is not there ,I understand that this happening because the message property is not owned by e2.

My question is what is the best way to make it so such that the result is {id:12,message:'message'}

I understand that it may be very novice question, but I appreciate your elaboration on it

use case

I have a global error handler below I need to copy err object into error object if in production mode, but I found that I need to manually add this line error.message = err.message;

I was wondering if there is away to clone err object without manually add this line ie what is the best way of cloning an object without knowing its super class properties?

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

As established in the comments, this has less to do with inheritance and more with message not being enumerable . It's still an own property of each instance.

You can change that in your custom subclass though with 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});

This will cause the spread syntax in the object literal to copy the property. However, if your goal is to get a new instance with the same custom prototype, you cannot use spread syntax. Write a method instead:

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

The best way to set up a prototype is to not do this with object spread as object literal produces a plain object that inherits from Object .

A way to fix this is:

Object.setPrototypeOf(e2, e)

A more correct way to do this is to create an object with proper prototype:

e2 = Object.create(e1)

To shadow inherited properties on assignment it can be combined with Object.assign to provide them as a plain object.

None of these will produce {id:12,message:'message'} output. message is non-enumerable property declared on Error.prototype that is used by Error.prototype.toString to stringify an error.

If the intention is to not use prototypal inheritance but produce {id:12,message:'message'} plain object, it needs to be:

e2 = {...e1, message: e1.message}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM