重点是Object.defineProperty,Vue.js就是基于它实现「响应式系统」的。
1、先说一下Object.defineProperty这个方法吧
首先要知道对象的每个属性都有一个描述对象(descriptor)用于控制该属性的行为。对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值:
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。
也就是说描述符只能是两种之一,要么是数据描述符要么是存取描述符,他们同时有的属性是configurable和enumerable,专属于数据描述符的是value和writable,专属于存取描述符的是set和get
而数据响应式主要用的就是存取描述符,当访问一个对象属性,就会调用里面的get方法,当设置一个对象属性,也会调用这个属性的set方法
使用方法
/*
obj: 目标对象
prop: 需要操作的目标对象的属性名
descriptor: 描述符
return value 传入对象
Object.defineProperty(obj, prop, descriptor)
*/
如:
obj={a:'123'}
var bvalue;
Object.defineProperty(obj, a,{
get : function(){
return bvalue;
},
set : function(newValue){
bvalue = newValue;
},
enumerable : true,
configurable : true
})
obj.a=111 //会调用set方法把111传进去赋值给bvalue,现在obj是{a:Getter}
obj.a //111
2、实现observer(可观察的)
vue在init的时候会进行初始化,会对数据进行响应式化。
具体怎么实现的呢?
emm,不太好讲,大概就是首先实例化一个vue实例的时候,在vue的构造函数中会调用观察者observer将里面的对象遍历,将每个属性都应用响应式方法(defineReactive),给它们都加上get和set描述符,就实现了基本的数据响应了。主要就是Object.defineProperty方法
let o = new Vue({
data: {
test: "I am test."
}
});
o._data.test = "hello,world."; /* 视图更新啦~ */
class Vue {
/* Vue构造类 */
constructor(options) {
this._data = options.data;
observer(this._data);
}
}
function observer (value) {
if (!value || (typeof value !== 'object')) { return; }
Object.keys(value).forEach((key) => { defineReactive(value, key, value[key]); });
}
function defineReactive (obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true, /* 属性可枚举 */
configurable: true, /* 属性可被修改或删除 */
get: function reactiveGetter () {
return val; /* 实际上会依赖收集,下一小节会讲 */
},
set: function reactiveSetter (newVal) {
if (newVal === val)
return;
cb(newVal);
}
});
}
function cb (val) {
/* 渲染视图 */
console.log("视图更新啦~");
}