1 、 defineProperty 重定义对象
JS原生es5版本提供对象重新定义的接口 defineProperty
defineProperty 可以修改对象的访问器属性,对象属性值发生变化前后可以触发回调函数。
对象的访问器属性包括 2 种类型:数据描述符、 存取描述符
1.1 数据描述符
value:对象key的值,默认是 空字符串 ''
writeable:是否可写,默认 true
configurable:true是否可配置,默认 true
enumerable:true是否可枚举, 默认 true
Object.getOwnPropertyDescriptors(obj);
{
k:{
configurable: true,
enumerable: true,
value: 90,
writable: true
}
}
1.2 存取描述符
set:function(){}属性访问器 进行写操作时调用该方法
get:function(){}属性访问器 进行读操作时调用该方法
属性描述符: configurable 、enumerable
configurable 、enumerable、 set 、 get
对象中新增key的value发生变化时会经过set和get方法。
var obj = {};var temp = '';
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get: function () {
return temp;
},
set: function (newValue) {
temp = newValue
}
}); // 需要维护一个可访问的变量 temp
修改后的 get/set 不使用 defineProperty 方法,可以写在 obj对象内,如下:
var obj = {
tempValue: 'duyi',
get name () {
return this.tempValue;
},
set name (newValue) {
this.tempValue = newValue;
}
};
obj.name = 10;
console.log( obj.name ); // 10
小结一次:到这里来基本上知道 getter setter是可以实现的,基于这个简单理论作出一个复杂逻辑。
2 、 Observer 源码
/**
* Observer类方法将对象修改为可被观察。
* 一旦应用了这个类方法, 对象的每一个key会被转换成 getter/setter
* 用于收集依赖项和触发更新。
*/
var Observer = function Observer (value) {
this.value = value; // 保存被观察的值到方法的实例属性
this.dep = new Dep(); // 建立一个Dep实例
this.vmCount = 0; // vmCount 记录vm结构的个数
def(value, '__ob__', this); // value对象添加 ‘__ob__’ key,并赋值 this
if (Array.isArray(value)) { // value对象如果是数组
if (hasProto) { // 表示在{}上可以使用 __proto__,只有此处用到,这还有不能用的地方么,nodejs也可以。
protoAugment(value, arrayMethods); // 将arrayMethods 放在value的__proto__ 原型上。arrayMethods这个方法有点深,好像是重写了数组的一部分原型方法
} else {
copyAugment(value, arrayMethods, arrayKeys); // 说不定真不支持 __proto__ ,调用def方法拷贝增强对象。
}
this.observeArray(value);
} else {
this.walk(value);
}
};
/**
* 遍历所有属性将它们转换为 getter/setter,仅当值类型为对象时调用。
*/
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]); // defineReactive$$1 () 方法,这个方法才是实现 getter / setter 的原方法!!!
}
};
/**
* 观察数组项列表
*/
Observer.prototype.observeArray = function observeArray (items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]); // observe 方法
}
};
// 只在 Observer有用到此方法
/**
* 截获原型链使用 __proto__ 的方式来
* 增强一个目标的对象或数组,简称原型增强。
*/
function protoAugment (target, src) {
/* eslint-disable no-proto */
target.__proto__ = src;
/* eslint-enable no-proto */
}
/**
* 增加对象原型properties
*/
function copyAugment (target, src, keys) {
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
def(target, key, src[key]);
}
}
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
/**
* 这里是实现getter setter的关键代码 * 这里是实现getter setter的关键代码 * 这里是实现getter setter的关键代码 * * 在对象上定义一个 有反应的原型 * 传参:obj对象,key关键字,val值,customSetter在set的时候会被执行(第4个参数),shallow 默认为undefined,为 true 时不执行 observe 函数。*/
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// 预定义的getter和setter
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
var childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
}
/**
* Dep 是可以有多个指令订阅的可观察对象,目的就是对一个目标深层处理
*/
var uid = 0;
var Dep = function Dep () {
this.id = uid++; // 添加了2个实例属性,id 用于排序和 subs 数组统计sub
this.subs = [];
};
// 在 subs中添加 sub
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
// 从 subs中移除 sub
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
};
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this); // addDep 是 Watcher 的原型方法,用于指令增加依赖
}
};
Dep.prototype.notify = function notify () {
// 稳定订阅列表
var subs = this.subs.slice();
if (!config.async) {
// 如果不是在异步运行,在程序调度中 subs 不可以被排序!
// 然后排序以确保正确的顺序。
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
// 然后顺序触发 Watcher 原型的 update 方法
subs[i].update();
}
};
// 当前目标程序被评估,这个评估全局唯一,一次只要一个观察者可以被评估
Dep.target = null;
var targetStack = [];
// 将目标程序推送到目标栈
function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
}
// 执行出栈先去掉
function popTarget () {
targetStack.pop();
Dep.target = targetStack[targetStack.length - 1];
}
// def 方法比较简单
/**
* 定义一个原型
* obj
* key 对象的key关键字
* val 对象的value值
* 是否可枚举,默认可写可配置
*/
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}