数据劫持

定义

访问或者修改对象的某个属性时,在访问和修改属性值时,除了执行基本的数据获取和修改操作意外,还基于数据的操作行为,以数据为基础去执行额外的操作

为什么要进行数据劫持

减少dom操作

demo:实现input输入的时候,改变p标签的值

1
2
<input type="text" id="text">
<p id="show"></p>

普通方法:

在input输入的时候,获取p的值,发现改变再操作dom修改p标签的值

使用数据劫持,定义对象,对象属性值发生改变,在操作dom修改p标签的值

实现原理

  • defineProperty,此方法详解见此章节

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    var text = document.getElementById('text')
    var show = document.getElementById('show')
    var oData = {
    name: '1'
    }
    text.oninput = function() {
    oData.name = this.value
    }
    function upData() {
    show.innerHTML = oData.name
    }
    upData()
    function Observer(data) {
    if(!data || typeof data != 'object') {
    return data
    }
    // Object.keys(data)不能获取数组的索引,所以Observer无法实现数组数据监听
    Object.keys(data).forEach(item => {
    definedReactive(data, item, data[item])
    })
    }
    function definedReactive(data, key, val) {
    Observer(val) //递归深度监听数据变化
    Object.defineProperty(data, key, {
    get() {
    return val
    },
    set(newValue) {
    if(val == newValue) {
    return
    }
    val = newValue
    upData()
    }
    })
    }
    Observer(oData)
  • Proxy

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    var text = document.getElementById('text')
    var show = document.getElementById('show')
    var oData = {
    name: '1'
    }
    var handler = {
    get() {

    },
    set: function(obj, prop, value) {
    // obj[prop] = value
    Reflect.set(obj, prop, value);
    upData()
    return true
    }
    }
    upData()
    var proxy = new Proxy(oData, handler)
    text.oninput = function() {
    proxy.name = this.value
    }
    function upData() {
    show.innerHTML = oData.name
    }

两种方法优缺点,defineProperty不能处理数组,proxy时es6的语法兼容性有要求,并且不能递归处理

参考文章