## Object.definedProperty()
### `1. 基本方法`
```js
let book = {};
Object.definedProperty(book, 'name', {
value: 'John',
writable: false,
configurable: false,
enumerable: false,
get() {
return newValue;
},
set(newValue) {
book.name = newValue;
},
});
```
### `2. 属性作用`
- `value`:为当前设定的对象`book`的属性`name`的值,此处也可以设定默认值。
如果不设定默认为`undefined`
- `writable`:能否修改对象`book`的属性`name`的值。
- `true`: 代表当前的值可通过`赋值的方式(见下文)`和`属性重定义的方式(见下文)`的方式可进行修改
- `false`: 和 `true` 的情况相反,也就是不能修改对象的属性的值
- `configurable`:表示当前对象的属性能否被删除。
- `true`: 可以删除对象的属性
- `false`: 不能删除对象的属性
- `enumerable`: 定义道德对象的属性是否可以在 `for...in` 循环和 `Object.keys()` 中进行遍历。
- (1) `value` 属性
```js
let person = {};
// 赋值的方式
person.name = 'rose';
// 属性重定义的方式
Object.defineProperty(person, 'name', {
value: 'jack',
});
```
- (2) `writable` 属性
```js
// 情况一:writable: false
let person = {};
Object.defineProperty(person, 'name', {
value: 'jack',
writable: false,
});
person.name = 'rose';
console.log('赋值方式person.name', person.name); // jack
Object.defineProperty(person, 'name', {
value: 'jack',
writable: false,
});
console.log('属性重定义方式person.name', person.name); // jack
// 情况二:writable: true
let person = {};
Object.defineProperty(person, 'name', {
value: 'jack',
writable: true,
});
person.name = 'rose';
console.log('赋值方式person.name', person.name); // rose
Object.defineProperty(person, 'name', {
value: 'john',
});
console.log('属性重定义方式person.name', person.name); // john
```
- (3)`configurable` 属性
```js
// 情况一:configurable: false
let person = {};
Object.defineProperty(person, 'name', {
value: 'jack',
configurable: false,
});
delete person.name;
console.log('person', person); // person {name: jack}
// 情况二:configurable: true
let person = {};
Object.defineProperty(person, 'name', {
value: 'jack',
configurable: true,
});
delete person.name;
console.log('person', person); // person {}
```
- (4) `enumerable` 属性
```js
let person = {};
Object.defineProperty(person, 'name', {
value: 'jack',
enumerable: true,
});
person.gender = 'man';
Object.defineProperty(person, 'age', {
value: '26',
enumerable: false,
});
console.log(Object.keys(person)); // [0:'name',1:'gender']
for (const key in person) {
console.log(key);
} // name gender
```
- (5) `get()`方法
- 属性的 `getter` 函数,如果没有 `getter` ,则为 `undefined`。 当访问改属性时,会调用此函数,
执行时不需要传入任何参数,但是默认会传入 `this` 对象(由于继承关系,这里的 `this` 指向的 `this`
指向不一定是定义改属性的对象)。该函数的返回值会被用作该属性的值。
- (6) `set()` 方法
- 属性的 `setter` 函数,如果没有 `setter` ,则为 `undefined`。当属性值被修改时,会调用此函数。该
方法接受一个参数(也就是被赋予的新值),会传入赋值的 `this` 对象。
### `3. 原理实现`
- 在 MVVM 框架中,一是监听数据的变化,二是数据驱动。
- 在通常使用中我们使用 `Object.definedProperty()` 来实现监听数据的变化,或者使用 `Proxy` 和 反射。
- 本章通过使用 `MutationObserver` 来实现 `Object.definedProperty()` 对数据变化的监听。
- (1) `API 的使用`
- 定义:`MutationObserver` 构造函数对 DOM 树所做的更改提供了监听的能力。替代了旧的 `MutaionEvents`
功能。
- 返回值:`MutationObserver` 创建后并返回一个 `new MutationObserver`,当 DOM 发生改变时,会调用
制定的回调函数。
- 注意点:
- `MutationObserver` API 是异步触发的,DOM 的改变并不会马上触发,而是等到所有对 DOM 改变的操作
完成后,整体的触发一次。
- 面对多次的 DOM 修改,`MutationObserver` 会将多次的改变记录封装成一个数组进行处理,而不是一条
一条进行处理。
- `MutationObserver` 在不影响浏览器性能的情况下响应 DOM 的更改。
- 基本方法:
- `disconnect()`:停止 `MutationObserver` 的监听,直到再次 `observe()` 再次被调用。
- `observe()`:开始监听,并通过回调函数让 `MutationObserver` 接受通知。
- `takeRecords()`:从 `MutationObserver` 的通知队列中移除所有挂起的通知,并在一个新`Array`的
`MutationObserver` 对象中返回它们。
- (2) `代码实现`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Object.definedProperty的实现</title>
</head>
<body>
<div id="app">
<h1 id="h"></h1>
</div>
</body>
</html>
<script>
// 选择要观察的节点
const targetNode = document.getElementById('app');
// 具体要观察节点那些改变
const config = { attributes: true, childList: true, subtree: true };
// 创建一个回调函数的实例,节点发生改变时执行的回调函数
const Observer = new MutationObserver((mutationList, observer) => {
mutationList.forEach((item, index) => {
if (item.type === 'childList') {
console.log('有节点发生改变,当前节点的内容是:', item.target.innerHTML);
} else if (item.type === 'attributes') {
console.log('修改了' + item.attributeName + '属性');
}
});
});
// 开始观察节点是否发生变化
Observer.observe(targetNode, config);
// 停止观察
// observer.disconnect();
</script>
```
- (3) `输出结果`
<img src='./Object.definedProperty.png'>