当前位置:   article > 正文

使用Object.defineProperty进行数据劫持,实现响应式原理-剖析vue2.0_object.defineproperty get set 数据劫持

object.defineproperty get set 数据劫持

数据响应式是vue的特性之一,在面试过程中也会常常被问起响应式原理,现在就让我们深入了解一下vue2.0中如何实现响应式,

下图是Vue2.0中对响应式原理的描述,其核心就是使用Object.defineProperty中的get/set进行数据劫持,
虽然Vue3.0中使用Proxy(代理)去实现响应式,其实原理都差不多,在3.0中主要是使用Proxy的get和set实现响应式,如果理解defineProperty,Proxy也会很快理解的

在这里插入图片描述

Object.defineProperty是什么?

define是定义的意思 Property是属性/描述的意思
其实defineProperty就是定义属性
主要有value、writable、configurable、enumerable、get、set这几个配置项
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

let object1={
	property1:0
}
//例如
Object.defineProperty(object1, 'property1', {
    value: 42,
    writable: false, //是否可写
    configurable: false, //是否可删除
    enumerable: false, //是否可枚举(遍历)
});
//get set配置项
Object.defineProperty(object1, 'property1', {
    get() {//get方法在进行获取的时候触发
        return property1
    },
    set(newVal) {//get方法在进行设置的时候触发,主要在此实现数据劫持
        property1 = newVal
    }
});

//我这边常用一种复数形式Object.defineProperties
Object.defineProperties(object1, {
    property1: {
        get() { //get方法在进行获取的时候触发
            return property1
        },
        set(newVal) { //get方法在进行设置的时候触发,主要在此实现数据劫持
            property1 = newVal
        }
    }
})
  • 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

使用上述的内容实现Vue2.0中的双向绑定

实现出来的效果

在这里插入图片描述

下面使用Object.defineProperty实现一个响应式计算器,
核心部分在:数据被修改(set)时重新计算结果

html部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>响应式计算器</title>
    <style>
        .btnGroup > button.active{
            background-color: orange;
            color: #fff;
        }
    </style>
</head>
<body>
    <div id="computed">
        <div class="result">0</div>
        <div class="inputGroup">
            <input type="number" class="ipt1" value="0" />
            <br/>
            <input type="number" class="ipt2" value="0" />
        </div>
        <div class="btnGroup">
            <button data-field="add" class="active">+</button>
            <button data-field="sub">-</button>
            <button data-field="mul">*</button>
            <button data-field="div">/</button>
        </div>
    </div>
    <!-- 引入js文件 -->
    <script src="./js/index.js"></script>
</body>
</html>
  • 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

js部分

class Arithmetic { //定义算法类
    add(a, b) {
        return a + b
    }
    sub(a, b) {
        return a - b
    }
    mul(a, b) {
        return a * b
    }
    div(a, b) {
        return a / b
    }
}

class Computed extends Arithmetic {
    constructor() {
        super();
        this.result = document.getElementsByClassName('result')[0]; //获取结果dom
        this.ipt1 = document.getElementsByClassName('ipt1')[0]; //获取输入框
        this.ipt2 = document.getElementsByClassName('ipt2')[0];
        this.btnGroup = document.getElementsByClassName('btnGroup')[0];
        this.btnItems = this.btnGroup.getElementsByTagName('button');
        this.data = this.defineData();
        this.btnIndex = 0; //记录选中btn索引
    }
    // 1.进行初始化
    init() {
        this.bindEvent();
    }

    // 2.绑定事件
    bindEvent() {
        this.btnGroup.addEventListener('click', this.onFieldBtnClick.bind(this), false)
        this.ipt1.addEventListener('input', this.onNumberIpt.bind(this), false)
        this.ipt2.addEventListener('input', this.onNumberIpt.bind(this), false)
    }

    // 6.使用Object.defineProperty进行数据劫持
    defineData() {
        let _obj = {},
            field = 'add', //保存计算方法字段
            fNumber = 0, //inp1的value
            sNumber = 0; //inp2的value
        let that = this;
        // 进行数据劫持
        Object.defineProperties(_obj, {
            fNumber: {
                get() {
                    return fNumber
                },
                set(newVal) {
                    fNumber = newVal
                    // 重点:在fNumber改变之后计算结果,此处vue使用的是Dom diff算法
                    that.computedResult(fNumber, sNumber, field);
                }
            },
            sNumber: {
                get() {
                    return sNumber
                },
                set(newVal) {
                    sNumber = newVal
                    that.computedResult(fNumber, sNumber, field);
                }
            },
            field: {
                get() {
                    return field
                },
                set(newVal) {
                    field = newVal
                    that.computedResult(fNumber, sNumber, field);
                }
            }
        })
        return _obj
    }

    // 3.btn点击事件
    onFieldBtnClick(ev) {
        let e = ev || window.event,
            tar = e.target || e.srcElement,
            tarName = tar.tagName.toLowerCase();
        tarName === 'button' && this.update(tar);
    }

    // 4.btn进行选项卡切换并更改field
    update(target) {
        this.btnItems[this.btnIndex].className = '';
        this.btnIndex = [].indexOf.call(this.btnItems, target);
        target.className += ' active';
        this.data.field = target.getAttribute('data-field');
    }

    // 5.ipt改变(input)事件
    onNumberIpt(ev) {
        let e = ev || window.event,
            tar = e.target || e.srcElement,
            className = tar.className,
            val = Number(tar.value.replace(/\s+/g, '')) || 0; //去除input的空格
        switch (className) {
            case 'ipt1':
                // 改变fNumber触发set()将重新计算结果
                this.data.fNumber = val;
                break;
            case 'ipt2':
                // 改变sNumber触发set()将重新计算结果
                this.data.sNumber = val;
                break;
            default:
                break;
        }
    }

    // 7.使用继承算法类的方法进行结果计算
    computedResult(fNumber, sNumber, field) {
        this.result.innerHTML = this[field](fNumber, sNumber)
    }
}

new Computed().init();
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

往期精彩文章

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/259056
推荐阅读
相关标签
  

闽ICP备14008679号