Не удается присвоить значение свойству '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"