observifyArray
by jin5354
HTML
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Babel + JSX
// more detail go to
// https://github.com/jin5354/leaf-observable
// Dep 类,保存数据源的所有订阅,并在接收到数据源的变动通知后,触发所有订阅
class Dep {
constructor() {
this.subs = []
}
addSub(sub) { // 添加订阅
this.subs.push(sub)
}
notify(newValue, oldValue) {
this.subs.forEach(sub => {
sub.update(newValue, oldValue) // 触发订阅
})
}
}
const globalDep = new Dep()
// Watcher 类,每个 Watcher 为一个订阅源
class Watcher {
constructor(callback) {
this.callback = callback
}
update(newValue, oldValue) {
this.callback(newValue, oldValue) // 被触发后执行回调
}
}
// 使一个对象响应化
function observify(value) {
if(!isObject(value)) {
return
}
if(Array.isArray(value)) { // 由于性能问题,我们不再对数组的每个 key 执行 Object.defineReactive
observifyArray(value)
for(let i = 0; i < value.length; i++) {
observify(value[i])
}
}else {
Object.keys(value).forEach((key) => {
defineReactive(value, key, value[key]) // 遍历每个键使其响应化
})
}
}
function isObject(value) {
return typeof value === 'object' && value !== null
}
//为对象的一个键应用 Object.defineProperty
function defineReactive(obj, key, value) {
observify(value) // 递归
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return value
},
set: (newValue) => {
if(newValue === value) {
return
}else {
let oldValue = value
value = newValue
observify(newValue)
globalDep.notify(newValue, oldValue) // 变动时通知 Dep
}
}
})
}
// 数组响应化
function observifyArray(arr) {
const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] //需要变异的函数名列表
let arrayAugmentations = Object.create(Array.prototype) // 创建一个 __proto__ 到 Array.prototype 的 arrayAugmentations 对象
aryMethods.forEach(method => { // 在 arrayAugmentations 对象上将需要变异的函数重写
arrayAugmentations[method] = function(...arg) {
let oldValue = _.cloneDeep(this)
Array.prototype[method].apply(this, arg) // 执行默认操作
...