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指令,这个底层原理还是需要学一学的。
如果觉得写的好的,欢迎您给个点赞;如果觉得写的不好的有错误,欢迎留言评论!
上一篇: 计蒜客-dfs(深搜)-红与黑
下一篇: DFS(深搜/4/18)