object.definedProperty 和 proxy
object.defineProperty
- 缺点
- 定义属性的时候,定义的是普通的属性,但是后面将其强行转换为了数据属性操作符
- Object.defineProperty 无法监听新增属性,删除属性。
- 一次只能对一个属性监视,要是想对所有属性监听,需要递归监听
- 对于数组的push, unshift方法增加的元素,也无法监听
- 示例
- 监听单个属性
const obj = {
name: "hello",
age: 18,
height: 1.88
}
let _name = obj.name
Object.defineProperty(obj, "name", {
set: function(newValue) {
console.log(newValue)
_name = newValue
},
get: function() {
console.log(_name)
return _name
}
})
- 监听对象上的多个属性
const keys = Object.keys(obj)
for (const key of keys) {
let value = obj[key]
Object.defineProperty(obj, key, {
set: function(newValue) {
},
get: function() {
}
})
}
- 弊端
Object.keys(person).forEach(function, key) {
Object.defineProperty(person, key, {
enumerable: true,
configurable: true
})
get() {
return person[key]
},
set(val) {
person[key] = val
console.log(person[key])
}
}
上述代码中,会造成栈溢出,不断触发get属性
设置中转函数,就不会造成栈溢出,但是依然不能监听很多其他的属性
let person = {
name: '',
age: 0
}
function defineProperty(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`访问了${key}属性`)
return val
},
set(newVal) {
console.log(`${key}属性被修改为${newVal}了`)
val = newVal
}
})
}
function Observer(obj) {
Object.keys(obj).forEach((key) => {
definePRoperty(obj, key, obj[key])
})
}
Observer(person)
- 深度监听对象
function defineProperty(ojb, key, val) {
if(typeof val === 'object') {
observer(val)
}
Object.defineProperty(obj, key, {
get() {
console.log(key)
return val
},
set(newValue) {
val = newValue
console.log(val)
}
})
}
function Observer(obj) {
if (typeof obj !== "Object" || obj === null) {
return
}
Object.keys(obj).forEach((key) => {
defineProperty(obj, key, obj[key])
})
}
//如果set传入的是一个对象,
set(newVal) {
if(typeof newVal === 'object'){
observer(key)
}
console.log(`${key}属性被修改为${newVal}了`)
val = newVal
}
proxy
- 监听一个对象的相关操作,可以先创建代理对象proxy对象,之后改对象的所有操作,都可以通过代理对象来完成,代理对象可以监听对源对象进行的操作.在目标对象之前架设了一层拦截,外界对该对象的访问,都必须先通过这层连接,可以对外界的访问进行过滤和改写
-
语法
const p = new Proxy(target, handler)
target: 要使用proxy包装的目标对象,可以是任何类型的对象,包括原生数组,函数甚至是另一个代理
handler: 通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p的行为。如果handler没有设置任何拦截,就等同于通向原对象
-
示例
const obj = { name: '不知道啊' } const objProxy = new Proxy(obj, { set: function(target, key, newValue) { console.log(key, newValue) target[key] = newValue }, get: function(target, key) { console.log(key) return target[key] } }) console.log(objProxy.name) objProxy.name = "koko" console.log(objProxy.name)
let subject = ['高数'] let handler = { get(obj, key) { console.log('触发了get') return key in obj?obj[key]: '没有这门学科' }, set(obj, key, val) { console.log('触发了set') obj[key] = val return true } } let ProxyObj = new Proxy(subject, handler) console.log(ProxyObj) console.log(ProxyObj[1]) ProxyObj[0] = '大学物理' console.log(ProxyObj[0]) ProxyObj.push('线性代数') //调用了两次set和get方法 console.log(ProxyObj[1])
-
Getter Setter
- set函数四个参数:
target
property
value
receiver
target
: 目标对象property
: 讲被设置的属性keyvalue
: 新属性值receiver
: 调用的代理对象
- get函数的三个参数
target
: 目标对象 侦听的对象property
: 被获取的属性keyreceiver
: 调用的代理对象
- 示例
function foo(num1, num2) { console.log(this, num1, num2) } const fooProxy = new Proxy(foo, { apply: function(target, thisArg, otherArgs) { console.log("监听执行了apply操作"); target.apply(thisArg, otherArgs) }, construct: function(target, otherArray) { console.log("监听了new操作"); console.log(target, otherArray) return new target(...otherArray) } }) new fooProxy("aaa", "bbb")
- set函数四个参数:
-
proxy 的所有捕获器
- getPrototypeOf
- setPrototypeOf
- isExtensible()
- preventExtensions
- getOwnPropertyDescriptor
- defineProperty
- ownKeys
- has
- get
- set
- deleteProperty
- apply
- construct
reflect
- 是一个内置的对象,提供了JavaScript操作的方法,reflect不是一个函数对象,因此是不可构造和的,reflect所有的方法都是静态的
- 将object对象的一些明显属于语言内部的方法,放到reflect对象上,现阶段,某些方法同时在Object和Reflect。未来的方法会部署到Reflect对象上
- 修改某些object方法的返回结果,结果会变得更加合理,会返回true 或者flase
- 让Object操作都变成函数行为,某些object操作是命令式,可以将他们变成函数行为
- reflect对象的方法和proxy对象的方法一一对应,只要是proxy对象上的方法,都能在reflect上找到对应的方法,这就让proxy对象可以方便的调用对应的reflect方法,完成默认行为,作为修改默认行为的基础
- 示例
- set函数四个参数:
target
property
value
receiver
target
: 目标对象property
: 讲被设置的属性keyvalue
: 新属性值receiver
: 调用的代理对象
let obj = { _name: "why", set name(newValue) { console.log("this", this) this._name = newValue }, get name() { return this._name } } const objProxy = new Proxy(obj, { set:function(target, key, newValue, receiver) { console.log(target) console.log(key) console.log(newValue) console.log(receiver) const isSuccess = Reflect.set(target, key, newValue,receiver) if(!isSuccess) { throw new Error(`set ${key} failure`) } }, get: function(target, key, receiver) { } }) objProxy.name = 'lisi' console.log(objProxy.name)
-
这里我尝试对为什么要在这里使用reflect进行一下说明
- 不使用reflect的情况:设置
objProxy.name = 'lisi'
,被objProxy
代理拦截,这种情况下,代码应该是这样的
这里的target是指的是目标对象,key是键,将目标键设置为对应的值,设置的是obj对象,在obj对象中,会存在set方法,对键值进行设置,会触发set方法,输出this的值,这里的this是obj。然后再执行到这行代码set:function(target, key, newValue, receiver) { target[key] = newValue }
this._name = newValue
的时候,还是会对obj中的name属性进行设置,又会触发obj对象里面的set方法 - 使用reflect的情况:设置
objproxy.name = 'lisi'
,被objProxy
代理拦截,这种情况下,代码如上.通过反射调用obj的set方法,传入对象,键值,新值,以及proxy对象。再obj中,执行set方法, 这里的this指的是proxy方法,将proxy方法中的name进行赋值,再这个过程中,还会执行proxy中的set方法。 - 总而言之,不设置reflect,不传入receiver的情况下,this指向的是obj,当传入proxy的时候,这个this值得是proxy。
- 不使用reflect的情况:设置
- set函数四个参数:
- 参数解释
- receiver参数
如果源对象obj中有setter getter的访问器属性的时候,可以通过receiver来改变里面的thisconst objProxy = new Proxy(obj, { has: function(target, key) { return Reflect.has(target, key) }, set: function(target,key, value, receiver) { console.log("set捕获器",key) return Reflect.set(target, key, value,receiver) }, get: function(target, key, receiver) { console.log("get捕获器", key) return Reflect.get(target, key,receiver) }, deleteProperty: function(target, key) { return Reflect.deleteProperty(target, key) } })
- construct
可以不用new创建一个对象,第一个参数必须是函数,否则会出错
Reflect.construct(target, argumentsList[, newTarget])
这里的Animal调用了student的apply方法。但是这里还是和apply方法创建的函数有一点不同,apply创建的对象construtc指向的是undefiend, 而reflect.construct 创建的对象指向的是target。function Student(name, age) { this.name = name this.age = age } function Animal() { } const stu = Reflect.construct(Student, ['hello',"world"], Animal)
function Person(name, age) { this.name = name this.age = age } function Student(name, age) { const _this = Reflect.construct(Person, [name, age], Student) return _this } const stu = new Student("why", 18) console.log(stu) console.log(stu.__proto__ === Student.prototype)
- receiver参数