最近Vue.js特别火,对于它的mvvm的特性,笔者感到特别好奇,于是通过原作者的话了解到,Vue使用了JavaScript的 Object.defineProperty
函数来实现数据的双向绑定。
这里,我将记录该方法的学习成果,也算是一个总结吧  ̄へ ̄
首先看 MDN
上对该方法的描述:
Object.defineProperty(obj, prop, descriptor)
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
返回值
被传递给函数的对象。
var person = {}
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(person,'age',{
configurable:false,//configurable特性表示对象的属性是否可以被删除,以及除writable特性外的其他特性是否可以被修改。
enumerable:false,//对象属性是否可通过for-in循环,false为不可循环,默认值为true
writable:false,//对象属性是否可修改,false为不可修改,默认值为true
value:'17' //对象属性的默认值,默认值为undefined
});
//writable
person.age="71";
console.log(person);//17,不可修改value,而且在严格模式下会抛出错误
//enumerable
person.name = "Allen"; 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true
for(var i in person){
console.log(person[i]) //无结果,不可循环
}
//configurable
delete person.age
console.log(person.age)//17,不可删除
Object.defineProperty(person,'age',{
configurable:true //不可修改,将抛出错误
});
2.一般的 Setters 和 Getters
function Factory(){
var value = null;
var factory = [];
Object.defineProperty(this, 'temperature',{
get: () => {
console.log('get!');
},
set: val => {
value = val;
factory.push(val);
}
});
this.getFactory = () => factory;
}
var f = new Factory();
f.temperature; //打印 get!
f.temperature = 22;
f.temperature = 33;
console.log(f.getFactory()); //打印 [22,33]
可以看到,当数据获取或者改变时,会调用函数的get
或set
方法,如此,再辅以视图层的操作,便实现了数据的双向绑定
进一步,我们还没有满足:那Vue
是如何使用这个方法做到的呢?
为了解决这个疑问,我们不得不翻开Vue
的源代码一览究竟
翻开Vue源代码,可以看到里面普遍使用了Object.defineProperty
方法,但是里面拓展了Property来避开Object.defineProperty
,其实原理是一样的。如下面Vue
的源码片段所示
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
对于Vue来说,它并不是单纯地通过数据和dom节点的绑定来绑定数据,它在网页dom和accessor之间会有两层,一层是Wacher,一层是Directive。如下图所示
如果我们通过代码修改了a.b
的值,那么set
函数就会通知Watcher,再由Watcher通知Directive修改Dom里a.b
的值
那么,如果我们要来实现Vue的功能,代码我们该怎么写呢?这里有一个小小的例子供你参考
html页面:
<div id="app">
Hello World!
div>
主体部分
//模仿Vue操作dom的语法
//我们自己创建一个Vue函数
const Vue = function({el, data={message:''}}){
//绑定 get和set方法
Object.defineProperty(this,'message',{
get: () => {
return document.getElementById(el).innerHTML
},
set: val => {
document.getElementById(el).innerHTML = val
}
})
}
const v = new Vue({
el: 'app',
data: {
message: 'Wooooooooooo!'
}
})
v.message //返回 Wooooooooooo! 字符串
v.message = 'ohhhhhhhhhhh!' //将div内的内容变为 ohhhhhhhhhhh!
这样,我们就能进行数据交互了!
当然,Vue
源码会比我们的练习代码复杂得多,学无止境,衣带渐宽终不悔,为伊消得人憔悴