Object.defineProperty与Proxy
Object.observe()方法用于异步地监视一个对象的修改。当对象属性被修改时,方法的回调函数会提供一个有序的修改流。然而,这个接口已经被废弃并从各浏览器中移除。你可以使用更通用的 Proxy
对象替代。
写了两个版本,分别是使用 js 里的 Proxy (代理)和 Object.defineProperty 实现
两个版本都有各自的缺陷,大家可以按需自己选择自己需要的
- Proxy 不能监听源对象,只能监控代理对象。代理对象属性值是个对象时,也可以进听到变化。
- Object.defineProperty 有新增属性的时候,无法做到自动监听。属性值是个对象时,监听不到变化。每个属性都要处理一遍,费事。
基于Proxy
/**
* 使用 Proxy 来说实现被废弃的 Object.observe()
*
* @param {any} target
* @param {any} fnBind
* @returns
*/
var bind = function ( target, fnBind ) {
return new Proxy( target, {
set: function ( target, prop, value ) {
target[prop] = value
fnBind.call( target )
}
} )
}
var person = {
name: '12'
,age: '23'
}
var child = bind( person, function () {
console.log( 'bind: ', this.name )
} )
person.name = 333 //监听不到
child.name = 444 //可以监听到
console.log( person )
console.log( child )
child.name = {aa:11}
child.name = {aa:22} //可以监听到
使用Obeject.defineProperty
/**
* 使用 es5 的 Object.defineProperty 特性 来实现 Object.observe()
*
* @param {any} target
* @param {any} fnBind
* @returns
*/
var bind = function ( target, fnBind ) {
bind.targets = bind.targets || []
bind.cloneTargets = bind.cloneTargets || []
var targets = bind.targets
, closeTargets = bind.cloneTargets
, index = targets.indexOf( target )
bind.fnBinds = bind.fnBinds || []
var fnBinds = bind.fnBinds
if( index == -1 ) {
index = targets.length
targets.push( target )
closeTargets.push( Object.assign( {}, target ) )
fnBinds.push( [] )
}
var targetFnBinds = fnBinds[index]
targetFnBinds.push( fnBind )
for( var prop in target ) {
Object.defineProperty( target, prop, {
set: function ( val ) {
closeTargets[index][prop] = val
for( var i = 0; i < targetFnBinds.length; i ++ ) {
targetFnBinds[i].call( target )
}
},
get: function () {
return closeTargets[index][prop]
}
} )
}
return target
}
var person = {
name: '12'
,age: '23'
}
var child = bind( person, function () {
console.log( 'bind: ', this.name )
} )
person.name = 333
child.name = 444
child.name = 555
console.log( person )
console.log( child )
person.name = {aa:11}
person.name = {aa:22} //注意这里的修改监听不到
上面已经提到了如果对象的属性还是个对象,就无法监听到变化。同样,如果属性是个数组,也无法监听到数组的变化。Vue2中是自己hack实现了Array的一些方法,让其可以被监听,后面Vue3会切换使用Proxy
扩展阅读:实现双向绑定Proxy比defineproperty优劣如何?