Object.defineProperty(obj,prop,attributes)
在一个对象上定义一个新的属性,或者修改该对象的一个属性,并返回这个对象
三个参数
obj目标对象,就是需要添加或修改属性的对象
prop目标对象的属性,需要添加或修改的属性名
attributes,对象,键值对,目标对象的属性需要的值
var obj={}
var objprop=Object.create(null)
Object.defineProperty(obj,"key",objprop)
Object.defineProperty(obj,"key",{
value:"jjjjj"
})
给obj对象添加一个属性,键为"key"值为“jjjjj”或者
var obj={}
var objprop=Object.create(null)
objprop={value:"嘿嘿"}
Object.defineProperty(obj,"key",objprop)
Object.defineProperty(obj,"key",objprop)
都表示的意思一样,但是注意的一点是attibutes这个对象的内容的键是value,如果是其他的比如name,age。。。。添加的值是undefined。
vue源码当中
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, attributes:{
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
封装好的函数,添加或者修改
双向绑定的原理
双向绑定就是更新view层model层跟着更新,更新model层,view层跟着更新
实现思路
需要一个监听器observer
需要一个订阅者Watcher
需要一个解析器Compile
实现observer
核心还是object.defineProperty()方法,修改添加属性值,双向绑定这里是修改值,通过递归遍历每一项,将data当中的属性直到遍历到键值对为止,因为双向绑定绑定的肯定不是一个对象,而是对象里面的键值对
function defineReactive(data, key, val) {
observe(val); // 递归遍历所有子属性
var dep = new Dep();
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
if (Dep.target) {. // 判断是否需要添加订阅者
dep.addSub(Dep.target); // 在这里添加一个订阅者
}
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
dep.notify(); // 如果数据变化,通知所有订阅者
}
});
}
Dep.target = null;
实现Watcher
订阅者如何添加,通过i调用get就可以
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
this.value = this.get(); // 将自己添加到订阅器的操作
}
Watcher.prototype = {
update: function() {
this.run();
},
run: function() {
var value = this.vm.data[this.exp];
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
},
get: function() {
Dep.target = this; // 缓存自己
var value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
return value;
}
};
关联observer与Watcher
function SelfVue (data, el, exp) {
this.data = data;
observe(data);
el.innerHTML = this.data[exp]; // 初始化模板数据的值
new Watcher(this, exp, function (value) {
el.innerHTML = value;
});
return this;
}
实现Compile
获取dom元素
function nodeToFragment (el) {
var fragment = document.createDocumentFragment();
var child = el.firstChild;
while (child) {
// 将Dom元素移入fragment中
fragment.appendChild(child);
child = el.firstChild
}
return fragment;
}
遍历各个节点
function compileElement (el) {
var childNodes = el.childNodes;
var self = this;
[].slice.call(childNodes).forEach(function(node) {
var reg = /\{\{(.*)\}\}/;
var text = node.textContent;
if (self.isTextNode(node) && reg.test(text)) { // 判断是否是符合这种形式{{}}的指令
self.compileText(node, reg.exec(text)[1]);
}
if (node.childNodes && node.childNodes.length) {
self.compileElement(node); // 继续递归遍历子节点
}
});
},
function compileText (node, exp) {
var self = this;
var initText = this.vm[exp];
updateText(node, initText); // 将初始化的数据初始化到视图中
new Watcher(this.vm, exp, function (value) { // 生成订阅器并绑定更新函数
self.updateText(node, value);
});
},
function updateText (node, value) {
node.textContent = typeof value == 'undefined' ? '' : value;
}