Не удается присвоить значение свойству 'name' объекта '[object Object]', так как оно только для чтения
У меня есть следующий код, который вызывает ошибку только для свойства <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 ответ(ов)
Если вы получаете эту ошибку в Angular+Typescript+NgRX, вы можете использовать оператор расширения для создания поверхностной копии объекта с доступом только для чтения, чтобы сделать его читаемым. Однако стоит учитывать, что это может быть нежелательным в зависимости от вашей ситуации.
let x = [...y];
Если вы используете Redux / NgRX, есть вероятность, что ваш селектор возвращает объект только для чтения с ссылкой на хранилище, что может привести к исключениям при попытке изменить свойство этого объекта через привязку в шаблоне. В зависимости от вашей ситуации, вы можете создать глубокую копию, чтобы убрать ссылку на хранилище.
let x = JSON.parse(JSON.stringify(y));
Вы не можете изменить свойство 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
Вы можете использовать оператор распространения (spread operator) ES7+ или TypeScript для обновления или добавления свойств к объекту. В вашем примере код используется для обновления объекта obj, добавляя новое свойство name, которое является объектом с полями first и last. Вот как это работает:
obj = { ...obj, name: { first: 'hey', last: 'there' } }
Здесь ...obj создаёт поверхностную копию существующего объекта obj, а затем добавляется новое свойство name с указанным значением. Таким образом, если name уже существует в объекте, он будет перезаписан. Если name не существует, оно будет добавлено.
Этот подход удобен, так как позволяет избежать мутации исходного объекта и делает код более читабельным.
Скорее всего, вы используете объект только для чтения, который нельзя редактировать. Попробуйте использовать cloneDeep из библиотеки lodash.
const x = cloneDeep(y);
где y — это объект только для чтения, а x следует использовать вместо y в вашем коде.
Если вы получаете эту ошибку в Angular+TypeScript:
НЕПРАВИЛЬНО:
@Output whatever_var = new EventEmitter();
ПРАВИЛЬНО:
@Output() whatever_var = new EventEmitter();
Обратите внимание на то, что в аннотации @Output необходимо добавить круглые скобки. Это важно для корректного определения декоратора в Angular.
Как получить класс объекта в JavaScript?
Может ли объект автоматически удалить себя после достижения своей цели?
Где найти документацию по форматированию даты в JavaScript?
Как проверить, содержит ли массив строку в TypeScript?
Как остановить Babel от трансформации 'this' в 'undefined' и добавления "use strict"