Почему я не могу переопределить свойство в объекте Javascript?
Я создаю объект с помощью Object.create
, и я хочу добавить к нему свойства.
> var o = Object.create({});
undefined
> Object.defineProperty(o, "foo", {value: 43, enumerable: true});
{foo: 43}
> o
{foo: 43}
> o.foo
43
> for (var i in o) { console.log(i); }
foo
> Object.keys(o)
['foo']
> Object.defineProperty(o, "foo", {value: 43, enumerable: false });
TypeError: Cannot redefine property: bar
Q1) Почему я не могу переопределить свойство?
> o.__proto__
{}
> o.prototype
undefined
Q2) Почему прототип пуст? И почему эти 2 значения отличаются друг от друга, т.е. {}
vs undefined
?
Ответы
Ответ 1
Вы не можете переопределить свойство, потому что Object.defineProperty()
по умолчанию неконфигурируемые свойства, из docs:
конфигурируемый
true тогда и только тогда, когда тип этого дескриптора свойства может быть изменен и если свойство может быть удалено из соответствующего объекта. По умолчанию false.
Итак, это значение по умолчанию - false - вам нужно передать его configurable: true
, чтобы разрешить его.
Ответ 2
-
Свойства, определенные через Object.defineProperty()
, по умолчанию не- configurable
.
Чтобы их можно было переопределить или переконфигурировать, они должны быть определены с этим атрибутом, установленным на true
.
var o = Object.create({});
Object.defineProperty(o, "foo", {
value: 42,
enumerable: true,
configurable: true
});
console.log(o); // { foo: 42 }
Object.defineProperty(o, "foo", {
value: 45,
enumerable: true,
configurable: true
});
console.log(o); // { foo: 45 }
-
o.prototype
- undefined
, поскольку объекты обычно не имеют свойств prototype
.
Такие свойства найдены в конструкторе function
для экземпляров new
для наследования, примерно эквивалентного:
function Foo() {}
// ... = new Foo();
var bar = Object.create(Foo.prototype);
Foo.call(bar);
Объект, однако, знает об их объектах прототипа. Они ссылаются на внутреннее свойство [[Prototype]]
, которое __proto__
является/является неофициальным получателем/установщиком:
console.log(o.__proto__); // {}
Стандартизованный способ чтения [[Prototype]]
заключен в Object.getPrototypeOf()
:
console.log(Object.getPrototypeOf(o)); // {}
Ответ 3
Только когда оба записываемых и настраиваемых имеют значение false, произойдет "Невозможно переопределить свойство".
Если значение доступное для записи или настраиваемое равно true, ошибка исчезнет.
"use strict"
var obj = {};
Object.defineProperty(obj, "name",
{
value: "Fundebug",
writable: false,
configurable: false
})
Object.defineProperty(obj, "name",
{
value: "云麒"
}) // 'Uncaught TypeError: Cannot redefine property: name'
Поэтому jdphenix и Jonathan не совсем верны.
Ответ 4
Object.defineProperty(o, "foo", {value: 43, enumerable: true});
Эта строка определяет свойство foo
для объекта o
со значением: 43 и атрибутами enumerable:true
, writable:false
, configurable:false
. Если свойство существует, defineProperty
обновляет свои флаги. В противном случае он создает свойство с заданным значением и флагами; в этом случае, если флаг не указан, он принимается ложным.
Итак, здесь мы делаем наше свойство неконфигурируемым, поскольку флаг (или атрибут) configurable
равен false.
Создание собственности non-configurable
является дорогой с односторонним движением. Мы не можем изменить это обратно с defineProperty
.
Чтобы быть точным, неконфигурируемость накладывает несколько ограничений на defineProperty:
- Не могу изменить настраиваемый флаг.
- Не могу изменить перечисляемый флаг.
- Не могу изменить запись: ложь на истину (наоборот работает).
- Cantchange get/set для свойства аксессора (но может присваивать их при отсутствии).
Поэтому будьте осторожны с флагом configurable
в defineProperty
, всегда устанавливайте его в true в defineProperty, если вы хотите изменить перечисляемые и доступные для записи атрибуты (или флаг). Как только вы установите настраиваемое значение false, вы не сможете установить его в значение true.