目录
1、监听对象的操作方式一——Object.defineProperty
let obj = {
name: 'why',
age: 18
}
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get: function() {
console.log(`监听到obj对象的${key}属性被访问了`);
return value
},
set: function(newValue) {
console.log(`监听到obj对象的${key}属性被设置了`);
value = newValue
}
})
})
obj.name = 'lisi'
obj.age = 30
console.log(obj.name);
console.log(obj.age);
缺点:
- 首先,
Object.defineProperty
设计的初衷,不是为了去监听截止一个对象中所有的属性的。我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符。 - 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么
Object.defineProperty
是无能为力的。 - 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象。
2、监听对象的操作方式二——Proxy
在ES6中,新增了一个Proxy
类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的:
- 也就是说,如果我们希望监听一个对象的相关操作,那么我们就可以先创建一个代理对象(
Proxy
对象); - 之后对该对象的所有操作,都是通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作。
2.1、 基本操作
const obj = {
name: 'why',
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get: function(target, key) {
return target[key]
},
// 设置值时的捕获器
set: function(target, key, newValue) {
target[key] = newValue
},
// 监听in的捕获器
has: function(target, key) {
console.log(`监听到对象的${key}属性in操作`, target);
return key in target
}
})
objProxy.name = 'lisi'
objProxy.age = 30
console.log(obj.name); // lisi
console.log(obj.age); // 30
// in操作符
console.log('name' in objProxy); // true
2.2、Proxy对函数对象的监听
function foo() {}
const fooProxy = new Proxy(foo, {
apply: function(target, thisArg, argArray) {
console.log('对foo函数进行了apply调用');
return target.apply(thisArg, argArray)
},
construct: function(target, argArry, newTarget) {
console.log('对foo函数进行了new调用');
return new target(...argArry)
}
})
fooProxy.apply({}, ['abc', 'fff'])
new fooProxy('abc', 'ddd')
备注:Proxy详细讲解
3、Reflect
3.1、Reflect的作用
- Reflect是ES6新增的一个API,它是一个对象,字面的意思是反射。
- 那么这个Reflect有什么用呢?
- 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
比如Reflect.getPrototypeOf(target)
类似于Object.getPrototypeOf()
;
比如Reflect.defineProperty(target, propertyKey, attributes)
类似于Object.defineProperty()
;
- 如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
- 这是因为在早期的ECMA规范中没有考虑到这种对对象本身的操作如何设计会更加规范,所以将这些API放到了Object上面;
但是Object作为一个构造函数,这些操作实际上放到它上面并不合适;
另外还包含一些类似于in
、delete
操作符,让JS看起来是会有一些奇怪的;
所以在ES6中新增了Reflect
,让我们这些操作都集中到了Reflect对象上;
- 那么Object和Reflect对象之间的API关系,可以参考MDN文档:Reflect
3.2、Reflect和Proxy一起使用
Reflect中常见的方法和Proxy是一一对应的,也是13个。
const obj = {
name: 'why',
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get: function(target, key) {
console.log('get-----');
return Reflect.get(target, key)
},
// 设置值时的捕获器
set: function(target, key, newValue) {
console.log('set-----');
const result = Reflect.set(target, key, newValue)
if (result) {
console.log('属性更新成功');
} else {
console.log('属性更新失败');
}
},
})
objProxy.name = 'lisi'
console.log(objProxy.name);
3.3、receiver参数的作用
- 不使用receiver参数时
let obj = {
_name: 'why',
get name() {
return this._name
},
set name(newValue) {
this._name = newValue
}
}
const objProxy = new Proxy(obj, {
get: function(target, key, receiver) {
// receiver是创建出来的代理对象
console.log('get方法被访问', key, receiver);
console.log(objProxy === receiver); // true
return Reflect.get(target, key)
},
set: function(target, key, newValue) {
console.log('set方法被访问', key);
Reflect.set(target, key, newValue)
}
})
objProxy.name = 'lisi'
console.log(objProxy.name);
// set方法被访问 name
// get方法被访问 name Proxy {_name: 'lisi'}
// true
// lisi
- 使用receiver参数时
let obj = {
_name: 'why',
get name() {
return this._name
},
set name(newValue) {
this._name = newValue
}
}
const objProxy = new Proxy(obj, {
get: function(target, key, receiver) {
// receiver是创建出来的代理对象
console.log('get方法被访问', key, receiver);
console.log(objProxy === receiver); // true
return Reflect.get(target, key, receiver)
},
set: function(target, key, newValue, receiver) {
console.log('set方法被访问', key);
Reflect.set(target, key, newValue, receiver)
}
})
objProxy.name = 'lisi'
console.log(objProxy.name);
// set方法被访问 name
// set方法被访问 _name
// get方法被访问 name Proxy {_name: 'lisi'}
// true
// get方法被访问 _name Proxy {_name: 'lisi'}
// true
// lisi
3.4、Reflect中construct的作用
function Student(name, age) {
this.name = name;
this.age = age;
}
function Teacher() {
}
// 执行Student中的内容,但是创建出来的对象是Teacher对象
const teacher = Reflect.construct(Student, ['why', '18'], Teacher)
console.log(teacher); // Teacher {name: 'why', age: '18'}
console.log(teacher.__proto__ === Teacher.prototype); // true