Недавно пишу цикл "Полный анализ JavaScript API" (только что закончил писать String, а сейчас пишу Object, который можно штамповатьПолный анализ JavaScript API), если вы хотите пройти через все API, рекомендованные MDN, вы также можете подготовить документ для себя. Поскольку очки знаний, связанные с Object.defineProperty(), более сложны, они вынесены отдельно и размещены здесь, и вы можете делать кирпичики.
грамматика
defineProperty(o: any, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): any;
описывать
Используется для определения новых свойств или изменения существующих свойств объекта и возврата объекта.
параметр
-
oцель -
pИмя свойства или метода, которое необходимо определить (вы можете изменить существующие или добавить новые свойства или методы). -
attributesДескриптор атрибута, конкретные атрибуты следующие:
interface PropertyDescriptor {
configurable?: boolean;
enumerable?: boolean;
value?: any;
writable?: boolean;
get?(): any;
set?(v: any): void;
}
дескриптор свойства
В ECMAScript есть два типа свойств:数据属性а также访问器属性.
атрибут данныхвключать:[[Configurable]], [[Enumerable]], [[Writable]], [[Value]]
свойство доступавключать:[[Configurable]], [[Enumerable]], [[Get]], [[Set]]
Ключевые значения, которые дескрипторы атрибутов могут иметь одновременно
| configurable | enumerable | value | writable | get | set | |
|---|---|---|---|---|---|---|
| атрибут данных | Yes | Yes | Yes | Yes | No | No |
| свойство доступа | Yes | Yes | No | No | Yes | Yes |
Короче говоря, определитьvalueилиwritable, 一定不能有getилиset, и наоборот, в противном случае сообщается об ошибке.
Configurable
Если параметр configurable свойстваfalse, Так:
- не сможет удалить этот атрибут, т.е.
delete obj.xxxНедействительно, об ошибке будет сообщено непосредственно в строгом режиме.
// 非严格模式下删除一个"不可配置"的属性会返回false
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'yancey',
configurable: false,
});
delete obj.name; // false
// obj.name并没有被删除
obj.name; // yancey
// 严格模式下删除一个"不可配置"的属性直接报错
(function() {
'use strict';
var o = {};
Object.defineProperty(o, 'b', {
value: 2,
configurable: false,
});
delete o.b; // Uncaught TypeError: Cannot delete property 'b' of #<Object>
return o.b;
})();
- когда перечисляемый или записываемый
false, превратить их вtrueто сообщается об ошибке, но когда ониtrue, но может превратить их вfalse( Обратите внимание, что должно быть不可配置По предположению, если имущество可配置, enumerable и writable можно переключать между true и false)
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'yancey',
configurable: false,
writable: false,
});
// 当"writable"和"configurable"均为false时, 尝试将"writable"变为true会报错
// Uncaught TypeError: Cannot redefine property: name
Object.defineProperty(obj, 'name', { writable: true });
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'yancey',
configurable: false,
writable: true,
});
// 但"writable"可成功从true切换到false
Object.defineProperty(obj, 'name', { writable: false });
- Все равно изменить еще раз
getа такжеsetОба сообщат об ошибке, потому что значение свойства обоих является функцией, а в JS невозможно иметь одну и ту же функцию.
:::совет ОБЗОР
сложные типы данных в栈средний накопительимя данныхс однимадрес кучи, существует堆средний накопительАтрибутыа такжестоимостьПри обращении сначала получить адрес из стека, а потом взять соответствующее значение из кучи.
:::
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'yancey',
configurable: false,
});
// Uncaught TypeError: Cannot redefine property: name
Object.defineProperty(obj, 'name', { get: function() {} });
// Uncaught TypeError: Cannot redefine property: name
Object.defineProperty(obj, 'name', { set: function() {} });
- если только
writableправда, да任意重新定义значение, но когдаwritableКогда оно ложно, ему нужно увидеть конкретный тип данных.В первом примере, хотя настраиваемыйfalse, но пока доступен для записиtrue, вы можете переопределить значение; во втором примере значение равно基本数据类型, поэтому, когда вы снова определяете значение, вам просто нужно перезаписать исходное значение; третье значение в примере является сложным типом данных, также потому, чтокучапроблема и не может быть переназначен.
const obj = {};
Object.defineProperty(obj, 'name', {
value: [],
configurable: false,
writable: true,
});
// 任意重定义value不报错
Object.defineProperty(obj, 'name', { value: 123 }); // {name: 123}
// 任意重定义value不报错
Object.defineProperty(obj, 'name', { value: {}); // {name: {}}
const obj = {};
Object.defineProperty(obj, 'name', {
value: 123,
configurable: false,
writable: false,
});
// 当value是基本数据类型, 用原值覆盖不会报错
Object.defineProperty(obj, 'name', { value: 123 }); // {name: 123}
// 用其他值代替必然报错
Object.defineProperty(obj, 'name', { value: {}); // Uncaught TypeError: Cannot redefine property: name
const obj = {};
Object.defineProperty(obj, 'name', {
value: [],
configurable: false,
writable: false,
});
// 当value是复杂数据类型, 修改value必定报错, 同样是堆栈的原因
Object.defineProperty(obj, 'name', { value: [] }); // {name: 123}
Writable
если собственностьwritableустановить какfalse, то свойство нельзя использовать赋值运算符Изменено. Но если значение свойства является массивом, оно не будет затронутоpush, spliceэффекты других методов.
const obj = {};
Object.defineProperty(obj, 'hobby', {
value: ['girl', 'music', 'sleep'],
writable: false,
configurable: true,
enumerable: true,
});
// "writable: false"并不对push、shift等方法起作用
obj.hobby.push('drink');
obj.hobby; // ['girl', 'music', 'sleep', 'drink']
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 当 hobby 被"赋值"给一个空数组时, 此属性的属性值不会被改变
obj.hobby = [];
obj.hobby; // ['girl', 'music', 'sleep', 'drink']
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 而当使用"严格模式"时, 给一个"不可写"属性赋值将直接报错
(function() {
'use strict';
var o = {};
Object.defineProperty(o, 'b', {
value: 2,
writable: false
});
o.b = 3; // throws TypeError: "b" is read-only
return o.b; // 2
}());
Enumerable
Определяет, можно ли перечислить свойства объекта в циклах for...in и Object.keys().
const obj = {
name: 'yancey',
age: 18,
say() {
return 'say something...';
},
};
Object.defineProperty(obj, 'hobby', {
value: ['girl', 'music', 'sleep'],
enumerable: true,
});
Object.defineProperty(obj, 'income', {
value: '100,00,000',
enumerable: false,
});
// 以下迭代器均不能输出"不可枚举属性", 即 income 的相关信息
for (const i in obj) {
console.log(obj[i]);
}
Object.keys(obj);
Object.values(obj);
Object.entries(obj);
Getter & Setter
Getter — это функция, вызываемая при чтении свойства, Setter — это функция, вызываемая при установке свойства, и Setter будет иметь один параметр — установленное значение.
Следующий код создает объект obj, который определяет два атрибута name и _time, обратите внимание, что символ подчеркивания _time является общепринятой нотацией, используемой для обозначения只能通过对象方法访问的属性.
Свойство метода доступа time содержит функцию получения и функцию установки.被修饰Значение _time , на котором основан установщик被设置的值Изменить имя.
Поэтому, когдаobj.time = 2, имя станет+2Это распространенный способ использования свойств средства доступа, т. е. установка значения одного свойства приводит к изменению других свойств.
const obj = {
name: '',
_time: 1,
};
Object.defineProperty(obj, 'time', {
configurable: true,
get() {
return `default: ${this._time}s`;
},
set(newValue) {
if (Number(newValue)) {
this._time = newValue;
this.name = `我为${this.name}+${newValue}s`;
}
},
});
obj.time; // 'default: 1'
obj.time = 2; // 2
obj.name; // '我为+2'
Посмотрите на другой пример, угон через Object.definePropertyobj.input, введет значениеsetИдентифицировать какnameв теге.Здесь есть вкус Vue.js,рекомендую статьюРазбор принципа реализации Vue — как реализовать двустороннюю привязку mvvm.
<p>Hello, <span id='name'></span></p>
<input type='text' id='input'>
const obj = {
input: '',
};
const inputDOM = document.getElementById('input');
const nameDOM = document.getElementById('name');
inputDOM.addEventListener('input', function (e) {
obj.input = e.target.value;
})
Object.defineProperty(obj, 'input', {
set: function (newValue) {
nameDOM.innerHTML = newValue.trim().toUpperCase();
}
})
Наконец, взглянув на пример наследования, мы создаемPersonКонструктор, который включает в себя два параметра:firstNameа такжеlastName, этот конструктор предоставляет четыре свойства:firstName, lastName, fullName, species, мы хотим, чтобы первые три свойства изменялись динамически, а последнее свойство было постоянным и не могло изменяться.
Следующий код явно не дает желаемого эффекта:firstNameилиlastNameВремя,fullNameне обновляется в режиме реального времени;speciesСвойства могут быть изменены по желанию.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = this.firstName + ' ' + this.lastName;
this.species = 'human';
}
const person = new Person('Yancey', 'Leo');
// 虽然 firstName 和 lastName 被修改了, 但 fullName 仍然是 "Yancey Leo"
person.firstName = 'Sayaka';
person.lastName = 'Yamamoto';
// 我们定义了一个关于“人”的构造函数, 所以并不希望 species 被修改成 fish
person.species = 'fish';
// 当我们修改了 fullName, 也同样希望 firstName 和 lastName 被更新
person.fullName = 'Kasumi Arimura';
Поэтому мы перепишем этот пример, используя Object.defineProperty() Обратите внимание, что:Захваченные свойства должны быть помещены в прототип, Таким образом, даже если будет создано несколько экземпляров, не будет конфликтов, Таким образом, вы можете использовать его с уверенностью.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Object.defineProperty(Person.prototype, 'species', {
value: 'human',
writable: false,
});
Object.defineProperty(Person.prototype, 'fullName', {
get() {
return this.firstName + ' ' + this.lastName;
},
set(newValue) {
const newValueArr = newValue.trim().split(' ');
if (newValueArr.length === 2) {
this.firstName = newValueArr[0];
this.lastName = newValueArr[1];
}
},
});
const person = new Person('Yancey', 'Leo');
person.firstName = 'Sakaya';
person.lastName = 'Yamamoto';
person.fullName; // 'Sayaka Yamamoto'
person.fullName = 'Kasumi Arimura';
person.firstName; // 'Kasumi'
person.lastName; // 'Arimura'
person.species = 'fish';
person.species; // 'human'
расширять
Помимо Getter и Setter в Object.defineProperty() есть два похожих способа.
__defineGetter__ и __defineSetter__()
Метод __defineGetter__ может быть已经存在Объект устанавливает (создает или изменяет) свойства средства доступа, метод __defineSetter__ может связать функцию с указанным свойством текущего объекта, и когда это свойство будет назначено, будет вызвана функция, которую вы связываете.
var o = {};
o.__defineGetter__('gimmeFive', function() {
return 5;
});
o.gimmeFive; // 5
:::Опасность Эта функция нестандартна, старайтесь не использовать ее в продакшене!
Эта функция была удалена из веб-стандартов, хотя некоторые браузеры все еще поддерживают ее в настоящее время, но могут перестать поддерживать ее в будущем, пожалуйста, постарайтесь не использовать эту функцию. :::
получить синтаксис в литералах объектов
Синтаксис get в литерале объекта может использоваться только в新建一个对象при использовании.
var o = {
get gimmeFive() {
return 5;
},
};
o.gimmeFive; // 5
Ссылаться на
Нет Object.defineProperty вы вышли
img { margin: 0 auto; display: block; }