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优劣如何?



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部