0

Не удается присвоить значение свойству 'name' объекта '[object Object]', так как оно только для чтения

12

У меня есть следующий код, который вызывает ошибку только для свойства <code>name</code>. Я мог бы решить эту проблему, указав, что свойство <code>name</code> является записываемым в аргументах функции <code>Object.create</code>, но я пытаюсь понять, почему это происходит (и, возможно, существует более элегантный способ это исправить).

Вот код:

var BaseClass = function (data) {
  Object.assign(this, data);
}

var ExtendedClass = function () {
  BaseClass.apply(this, arguments);
}

ExtendedClass.prototype = Object.create(BaseClass);

console.log(new ExtendedClass({ type: 'foo' }));
new ExtendedClass({ name: 'foo' });

Когда я создаю новый экземпляр <code>ExtendedClass</code> с объектом, содержащим свойство <code>name</code>, код выбрасывает ошибку. Почему это происходит и как я могу это исправить?

5 ответ(ов)

0

Если вы получаете эту ошибку в Angular+Typescript+NgRX, вы можете использовать оператор расширения для создания поверхностной копии объекта с доступом только для чтения, чтобы сделать его читаемым. Однако стоит учитывать, что это может быть нежелательным в зависимости от вашей ситуации.

let x = [...y];

Если вы используете Redux / NgRX, есть вероятность, что ваш селектор возвращает объект только для чтения с ссылкой на хранилище, что может привести к исключениям при попытке изменить свойство этого объекта через привязку в шаблоне. В зависимости от вашей ситуации, вы можете создать глубокую копию, чтобы убрать ссылку на хранилище.

let x = JSON.parse(JSON.stringify(y));
0

Вы не можете изменить свойство name функции. Дескриптор говорит, что оно не является writable (записываемым).

var BaseClass = function (data) {
  Object.assign(this, data);
};

console.log(Object.getOwnPropertyDescriptor(BaseClass, 'name'));

Но поскольку оно configurable (настраиваемое), вы можете использовать Object.defineProperty().

var BaseClass = function (data) {
  Object.assign(this, data);
};

Object.defineProperty(BaseClass, 'name', {
  writable: true,
  value: 'Foo'
});

console.log(BaseClass.name);

EDIT

Я снова с вами! Так... Как я уже говорил в комментариях, я думаю, что идентифицировал вашу проблему. Я ответил немного слишком быстро и не заметил, что ваше наследование ES5 неверное.

ExtendedClass.prototype = Object.create(BaseClass); — это не то, что вам нужно. Сделав это, вы делаете так, что прототип ExtendedClass становится конструкцией функции. Это, очевидно, вызывает неожиданное поведение.

function BaseClass(data) {
  console.log(this instanceof BaseClass); // "this" не является экземпляром "BaseClass"
  console.log(this instanceof Function); // "this" — это функция
  console.log(this.name); // "this" — это "BaseClass"
  
  Object.assign(this, data);
}

function ExtendedClass() {
  BaseClass.apply(this, arguments);
}
ExtendedClass.prototype = Object.create(BaseClass);

new ExtendedClass({ type: 'foo' });

В вашем коде this — это функция и ссылается на BaseClass. Именно поэтому вам не разрешается изменять его имя...

Фактически, при работе с наследованием в JavaScript вам обычно нужны эти две строки:

ExtendedClass.prototype = Object.create(BaseClass.prototype);
ExtendedClass.prototype.constructor = ExtendedClass;

Вот корректная реализация:

function BaseClass(data) {
  console.log(this instanceof BaseClass); // "this" является экземпляром "BaseClass"
  console.log(this instanceof Function); // "this" — это не функция
  console.log(this.name); // "this" пока не имеет имени
  
  Object.assign(this, data);
}

function ExtendedClass() {
  BaseClass.apply(this, arguments);
}
ExtendedClass.prototype = Object.create(BaseClass.prototype);
ExtendedClass.prototype.constructor = ExtendedClass;

var instance = new ExtendedClass({ name: 'foo' });

console.log(instance.name); // foo
console.log(BaseClass.name); // BaseClass
console.log(ExtendedClass.name); // ExtendedClass
0

Вы можете использовать оператор распространения (spread operator) ES7+ или TypeScript для обновления или добавления свойств к объекту. В вашем примере код используется для обновления объекта obj, добавляя новое свойство name, которое является объектом с полями first и last. Вот как это работает:

obj = { ...obj, name: { first: 'hey', last: 'there' } }

Здесь ...obj создаёт поверхностную копию существующего объекта obj, а затем добавляется новое свойство name с указанным значением. Таким образом, если name уже существует в объекте, он будет перезаписан. Если name не существует, оно будет добавлено.

Этот подход удобен, так как позволяет избежать мутации исходного объекта и делает код более читабельным.

0

Скорее всего, вы используете объект только для чтения, который нельзя редактировать. Попробуйте использовать cloneDeep из библиотеки lodash.

const x = cloneDeep(y);

где y — это объект только для чтения, а x следует использовать вместо y в вашем коде.

0

Если вы получаете эту ошибку в Angular+TypeScript:

НЕПРАВИЛЬНО:

@Output whatever_var = new EventEmitter();

ПРАВИЛЬНО:

@Output() whatever_var = new EventEmitter();

Обратите внимание на то, что в аннотации @Output необходимо добавить круглые скобки. Это важно для корректного определения декоратора в Angular.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь