本文介绍`Object.defineProperty()`方法,并基于此简单讨论数据劫持的实现方案。
defineProperty
Object.getOwnPropertyDescriptor(target,attrName)
方法用于获取对象的属性描述符对象,该方法的第一个参数为目标对象,第二个参数为指定的属性名。
我们可以利用该方法来查看对象属性的描述符配置项(包括:value值
、writable可重写
、enumerable可枚举
和configurable可配置
等)。默认正常的对象属性中,这些配置项的值都是 true
。
1 2 3 4
| var o = { name: "文顶顶", age: 18 }; var des = Object.getOwnPropertyDescriptor(o, "name"); console.log(des);
|
Object.defineProperty(target,attrName,options)
方法用于定义(设置)对象并对指定的属性描述符对象进行配置。该方法的第一个参数为目标对象,第二个参数为指定的属性名,第三个参数为配置对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
var o = { name: "文顶顶", age: 18 }; Object.defineProperty(o, "address", { value: "香悦山" }); console.log(Object.getOwnPropertyDescriptor(o, "address"));
Object.defineProperty(o, "age", { value: 20, enumerable: false, writable: false }); console.log(Object.getOwnPropertyDescriptor(o, "age"));
o.age = 99; console.log(o.age); for (var key in o) { console.log(key, o[key]); }
|
Object.defineProperties(target,options)
方法用于一次性设置(添加
)对象的多个属性,与之对应的Object.getOwnPropertyDescriptors(target)
方法用于获取对象中所有成员的 详细 配置信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Object.defineProperties(o, { "className": { value: "H5", configurable: true, }, "friends": { value: ["胡适", "沈从文", "辜鸿铭"], configurable: true, writable: true } }); console.log("_____"); console.log(Object.getOwnPropertyDescriptors(o));
|
[Object.defineProperty ]()方法主要用于对象中的某个属性进行访问配置,如果需要对整个对象执行类似的操作则可使用`Object.preventExtensions()`、`Object.seal()`和`Object.freeze()` 等方法,它们分别对应着`禁止扩展`、`密封对象`以及要`冻结`对象。
Getter and Setter
对于对象字面量创建的对象而言,我们可以直接通过get attrName
或set attrName
的方式来对属性的设置和读取操作进行拦截和监听。通过下面的代码,我们可以观察到,对象属性的 Getter 和 Setter 的代码并不复杂但却需要借助一个无关的中间变量_age
来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var o = { name: "文顶顶", _age: 17, get age() { console.log("监听到执行了getter方法"); return this._age; }, set age(val) { console.log("监听到执行了setter方法"); this._age = val; } } console.log(o.age); o.age = 100; console.log(o.age);
|
Object.defineProperty()
方法的配置对象中也支持对象属性的 Getter
和 Setter
操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| (function() { var o = { name: "文顶顶", age: 17 }; var temp = 18; Object.defineProperty(o, "age", { get() { console.log("——getter———"); return temp; }, set(val) { console.log("——setter———"); temp = val; } }); console.log(o.age); o.age = 100; console.log(o.age);
})();
|
利用`Object.defineProperty()`方法,来监听对象属性的设置和读取操作,可以不必借助于中间属性来实现而改用一个外部变量即可,这样的处理方式为代码的封装提供了可能。
注意 在 defineProperty
方法内部使用 set 和 get
函数时不能与 value 和 writable
共存。上述的代码演示了监听对象单个属性读写的方案,如果需要为对象中所有的属性都添加 set 和 get
监听,可以考虑对上述代码进行封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| let observer = (target) => { if (typeof target !== "object" || target == null) return; for (var key in target) { if (target.hasOwnProperty(key)) defineReactive(target, key, target[key]); } }
let defineReactive = (target, key, val) => { observer(val); Object.defineProperty(target, key, { get() { handler("getter") return val; }, set(_val) { handler("setter") val = _val; } }) }
let handler = (text) => { console.log("监听到" + text); }
var o = { name: "文顶顶", age: 18, car: { color: "white" } }; observer(o); o.age = 100; console.log(o.age); o.car.color = "black"; console.log(o.car.color);
|