Vue中实现数据双向绑定的原理——Object.defineProperty()

Vue实现数据双向绑定主要是:

采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当一个普通JavaScript对象传给Vue实例data选项时,Vue将遍历它的属性,用Object.defineProperty()将它们转为getter/setter(用户看不到getter/setter),在内部它们让Vue追踪依赖,在属性被访问和修改时通知变化。

Vue的数据双向绑定将MVVM作为数据的入口,整合整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

js实现简单的双向绑定

<body>
    <div id="app">
    <input type="text" id="txt">
    <p id="show"></p>
</div>
</body>
<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>

以上内容是不是文邹邹的看不懂呢?没关系,接下来我们来深入了解Object.defineProperty() 的语法之后,就很简单了。

Object.defineProperty() 方法会直接在一个对象上定义一个新的属性,或者修改一个对象现有属性,并返回这个对象。

语法:

Object.defineProperty(obj, prop, descriptor)

参数:
1、obj,要在其上定义属性的对象。
2、prop,要定义或修改的属性的名称。
3、descriptor,将被定义或修改的属性描述符。((其中描述符对象的属性必须是: configurable、enumerable、writeable、和value)设置其中一个或多个值,可以修改对应的特性值)

数据属性

[[Configurable]] 表示能否通过delete删除属性或者重新设置属性,默认值为true。

[[Enumerable]] 表示能否通过(for…in 或 Object.keys 方法)返回属性。默认值为true。

[[Writable]] 表示能够修改属性的值。可不可以赋值。默认值为true 。

[[Value]] 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined。

writable的情况演示

var person = {
    name: 'zhang',
    age: 12
};
Object.defineProperty(person,"name",{
    writable: false //不可赋值
});
// 读取person.name
console.log(person.name);  // zhang
console.log(person.age); //12
// 修改person.name
person.name = 'wang';
person.age = 14;
console.log(person.name);   // zhang  ?????? 咋没变
console.log(person.age);  // 14
// 原因: 那个配置里面也说了的[[Writable]]默认是true,
//但是改为false之后 就是这个属性不可赋值
//但是可以正常修改的其他的属性

enumerable的情况演示

var person = {
    name: 'zhang',
    baba: '1',
    job: '前端'
};
 
for(var key in person){
    console.log(key);  //   name, baba, job
    //默认为true 就可以成功遍历出来
}
//  三个属性都可以正常的打印出来<br>// 配置一下 enumerable
Object.defineProperty(person,'baba',{
    enumerable: false
    //baba 属性设置为false,不可遍历
});
 
for(var key1 in person){
    console.log(key1);   // name job
}
// ???   baba去哪了?  我告诉你去哪了  因为配置了 enumerable: false
console.log(person.baba);  // 1  不会影响输出

Configurable 的演示

var person = {
    name: 'zhang',
    age: 12
};
 
Object.defineProperty(person,'name',{
    configurable: false
    // 默认为true,设置成了false,
    // 意思是不可删除,不可配置
});
 // 打印还是可以正常打印的,这个不影响
console.log(person.name);   // zhang
console.log(person.age);    // 12
 
delete person.name;
console.log(person.name);  // zhang
// 刚才删除不是undefined吗  configurable: false就不允许删除了
// 如果声明是严格模式的话 那一句 delete person.name  还会报错
object.defineProperty(person,'name',{
	value: 'rose'
})
console.log(person.name); //Cannot redefine property: name 报错
person.name =  'wang'
//这里是通过属性定义的方式,可以修改name的属性值,没有配置它
console.log(person.name); // wang

这个地方需要注意一下,当这个东西一旦被配置为false后,以后再也不能把它变成true,会报错的

Value的演示

var person = {
    name: 'sheng'
};
console.log(person.name);  // zhang
Object.defineProperty(person,'name',{
    value: 'wang'
});
console.log(person.name); // wang
// 你小子怎么改姓了
person.name = 'sheng';  // 给我改回来
console.log(person.name);  //sheng  又是我盛家的孩子了,这个是可以修改回来的

当然,这几个可以结合起来一起使用的。

访问器属性

[[Get]]: 在读取属性的时候调用。 默认值 undefined
[[Set]]: 在写入属性的时候调用 默认值 undefined
get和set的演示

var book = {
_year: 2018,
edition: 1
};
 
Object.defineProperty(book,'year',{
get: function () {
    console.log(`获取的时候触发函数`);  // 每次获取的时候都会触发这个函数
    console.log(this === book); // true   这里面的this就是book的引用 也就是book对象
    return this._year;
},
set: function (newValue) {
    console.log('修改值的时候触发函数');
    // newValue ----- 这里的newValue就是你设置的修改后的值
    if(newValue> 2018){
        this._year = newValue;
        this.edition = newValue - 2018;
    }
}
});
 
console.log(book.year);
// 2018  他的值也就是我们返回的值 book._year
// 如果get函数不写返回值就是undefined
 
book.year = 2020; // 修改值的时候触发函数
console.log(book); // {_year: 2019, edition: 2};

Vue的双向数据绑定就是这个,当我们在修改数据的时候,会触发set那个函数,就在set函数中执行我们的逻辑,然后get函数会自动将最新的数据显示出去。
当然,在Vue中已经封装好了v-model指令,这个底层原理还是需要学一学的。

如果觉得写的好的,欢迎您给个点赞;如果觉得写的不好的有错误,欢迎留言评论!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值