监听对象-Object.defineProperty
案例
-
通过Object.defineProperty的存储属性描述符来对属性的操作进行监听
const obj = { name: "zzy", age: 22 }; // 监听对象中某一个key Object.defineProperty(obj, "name", { get: function () { console.log('被访问'); // 被访问 return 'bbb' }, set: function (value) { console.log('被设置', value); // 被设置 aaa }, }); console.log(obj.name); // bbb obj.name = "aaa"; // 监听对象中所有的key Object.keys(obj).forEach(key => { let value = obj[key] Object.defineProperty(obj, key, { get() { console.log(`${key}被访问`); return value }, set(newValue) { value = newValue console.log(`${key}的value被修改为${newValue}`); } }) }) obj.name = 'lll' obj.age = 23 console.log(obj.name); console.log(obj.age);
缺点
- Object.defineProperty设计初衷不是为了监听截止一个对象中的所有属性
- 在定义属性时,只是为了定义普通的属性,但是后面强行将数据属性描述符变为了访问属性描述符
- Object.defineProperty无法监听新增属性,删除属性等丰富的操作
监听对象-Proxy(ES6)
- ES6,新增了一个Proxy类,帮助我们创建一个代理
- 监听一个对象的操作之前,先创建一个代理对象(Proxy对象)
- 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行的操作
捕获器(trap)(13个)
set函数
- 属性设置
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
- value:新属性值
- receiver:如果遇到
setter,receiver则为setter调用时的this值。
get函数
- 属性读取
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
- receiver:如果
target对象中指定了getter,receiver则为getter调用时的this值。
has函数
- 是否含有(in)
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
deleteProperty函数
- 删除(delete)
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
getPrototypeOf
- 获取对象原型(Object.getPrototypeOf)
- target:目标对象(被监听的对象)
setPrototypeOf
- 设置对象原型(Object.getPrototypeOf)
- target:目标对象(被监听的对象)
- prototype:新原型对象
isExtensible
- 是否可以扩展(Object.isExtensible)
- target:目标对象(被监听的对象)
preventExtensions
- 阻止扩展(Object.preventExtensions)
- target:目标对象(被监听的对象)
getOwnPropertyDescriptor
- 获取属性描述符扩展(Object.getOwnPropertyDescriptor)
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
defineProperty
- 定义属性描述符扩展(Object.defineProperty)
- target:目标对象(被监听的对象)
- propertyKey:将被设置的属性key
- attributes:属性描述
ownKeys
-
获取属性名(Object.getOwnPropertyNames)
-
获取属性Symbol(Object.getOwnPropertySymbols)
-
target:目标对象(被监听的对象)
-
ArrayLike:属性
apply(监听函数对象)
- 函数调用
- target:目标函数(被监听的函数)
- thisArg:绑定的this
- argArray:参数
construct(监听函数对象)
- new操作
- target:目标函数(被监听的函数)
- argArray:参数
- newTarget:新目标函数(与target是同一个)
案例
-
new Proxy对象,传入需要监听的对象以及一个处理对象(handler)
const p = new Proxy(target, handler) -
直接操作Proxy对象,而不是原对象,可以通过handler监听
监听普通对象
// 监听普通对象
const obj = {
name: 'zzy',
age: 22
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get(target, key) {
console.log(`监听到`, target, `的${key}属性被访问`);
return target[key]
},
// 设置值时的捕获器
set(target, key, newValue) {
console.log(`监听到`, target, `的${key}属性被修改为${newValue}`);
target[key] = newValue
},
// 监听in的捕获器
has(target, key) {
console.log(`监听到`, target, `的${key}属性执行in操作`);
return key in target
},
// 监听delete的捕获器
deleteProperty(target, key) {
console.log(`监听到`, target, `的${key}属性被删除`);
delete target[key]
}
})
// get
console.log(objProxy.name, objProxy.age); // zzy 22
// set
objProxy.name = 'lll'
objProxy.age = 23
// get
console.log(objProxy.name, objProxy.age); // lll 23
// in
console.log('name' in objProxy);
// delete
delete objProxy.age
监听函数对象
// 监听函数对象
function foo() {}
const fooProxy = new Proxy(foo, {
// 监听apply的捕获器
apply(target, thisArg, argArray) {
console.log("对foo函数进行apply调用");
return target.apply(thisArg, argArray);
},
// 监听new操作的捕获器
construct(target, argArray, newTarget) {
console.log("对foo函数进行new调用", newTarget);
return new target(...argArray);
},
});
// apply
fooProxy.apply({}, ["aa", "bb"]); // 对foo函数进行apply调用
// new
new fooProxy("a", "b"); // 对foo函数进行new调用 [Function: foo]
Reflect(ES6)
-
早期ECMA没有考虑这种对对象本身的操作如何设计会更加规范,所以将这些API放到了Object上,但是Object作为一个构造函数,这些操作实际上放到它身上并不合适
-
ES6,新增了一个API,它是一个对象,reflect(反射)
- 提供很多操作JS对象的方法,类似Object中操作对象的方法
- 比如Reflect.getPrototypeOf(target)
- 比如Reflect.defineProperty(target, propertyKey, attributes)
- in, delete等操作符也被集中到了reflect对象上
- 提供很多操作JS对象的方法,类似Object中操作对象的方法
-
可以将Object对原对象的操作,修改为Reflect来操作
常见方法(13个)(与proxy对应)
Reflect.set(target,propertyKey,value[,receiver])
- 将值分配给函数的属性,更新成功,返回true
- target:目标对象
- propertyKey:将被设置的属性key
- value:新属性值
- receiver:如果遇到
setter,receiver则为setter调用时的this值。
get(target,propertyKey[,receiver])函数
- 属性读取/类似target[name]
- target:目标对象
- propertyKey:将被设置的属性key
- receiver:如果
target对象中指定了getter,receiver则为getter调用时的this值。
has(target,propertyKey)
- 是否含有某个属性(in)
- target:目标对象
- propertyKey:将被设置的属性key
deleteProperty(target,propertyKey)
- 删除(delete)
- target:目标对象
- propertyKey:将被设置的属性key
getPrototypeOf(target)
- 获取对象原型(Reflect.getPrototypeOf)
- target:目标对象
setPrototypeOf(target,prototype)
- 设置对象原型(Reflect.getPrototypeOf)
- target:目标对象
- prototype:新原型对象
isExtensible(target)
- 是否可以扩展(Reflect.isExtensible)
- target:目标对象
preventExtensions(target)
- 阻止扩展(Reflect.preventExtensions),返回boolean
- target:目标对象
getOwnPropertyDescriptor(target,propertyKey)
- 获取属性描述符扩展(Reflect.getOwnPropertyDescriptor)
- target:目标对象
- propertyKey:将被设置的属性key
defineProperty(target,propertyKey,attributes)
- 定义属性描述符扩展(Reflect.defineProperty)
- target:目标对象
- propertyKey:将被设置的属性key
- attributes:属性描述
ownKeys(target)
- 返回一个包含所有自身属性(不包含继承属性)的数组(类似于Object.keys(),但不会受enumerable影响)
- target:目标对象
apply(target,thisArg,argArray)
- 函数调用/类似function.prototype.apply()
- target:目标函数
- thisArg:绑定的this
- argArray:参数
Reflect.construct(target, argumentsList[, newTarget])
- new操作/new target(...argArray)
- target:目标函数
- argArray:参数
- newTarget:应使用其原型的构造函数
案例
-
const obj = { name: "zzy", age: 22, }; const objProxy = new Proxy(obj, { get(target, key, receiver) { return Reflect.get(target, key); }, set(target, key, newValue, receiver) { // 设置成功返回true const result = Reflect.set(target, key, newValue); // 根据是否设置成功 if (result) { } else { } }, }); objProxy.name = "aaa"; console.log(objProxy.name);
Receiver
-
如果源对象(obj)有getter/setter的访问器属性,就可以通过receiver改变访问器属性的this指向为代理对象
-
receiver是创建出来的代理对象
const obj = { _name: 'zzy', get name() { // 这里的this是obj对象 return this._name }, set name(newName) { this._name = newName } } const objProxy = new Proxy(obj, { get(target, key, receiver) { console.log('get被访问', key); // receiver是创建出来的代理对象(即objProxy), // receiver可以将原对象getter/setter的this指向改为代理对象 return Reflect.get(target, key, receiver) }, set(target, key, newValue, receiver) { console.log('set被访问', key); Reflect.set(target, key, newValue, receiver) } }) objProxy.name ='aa' console.log(objProxy.name);
Construct
-
Reflect.construct(target, argumentsList[, newTarget])
-
改变目标函数的原型
-
function Student(name, age) { this.name = name; this.age = age; } function Teacher() {} // 需求:执行Student函数的内容,但是创建出来的对象是Teacher 对象 const teacher = Reflect.construct(Student, ['zzy', 22], Teacher) console.log(teacher); // Teacher { name: 'zzy', age: 22 } console.log(teacher.__proto__ === Teacher.prototype); // true