本文已参与「新人创作礼」活动,一起开启掘金创作之路。
对于Object.defineProperty作用
// 这是一个普通的数据对象
let obj = {a : 10}// 先定义一个变量作为初始值,
let val = obj.a
Object.defineProperty(obj, a, {
get() {
// 当访问obj中a属性会触发这里(getter)
return val
},
set(newValue) {
if (val === newValue) {
return
}
// 当修改obj中的a 属性就需要访问这里(setter)
val= newValue
console.log("改变a属性为", newValue)
},
enumerable: true,
}
)
这时候我们会发现,当前这个方法只对a属性有效,当某一个object对象有多个key时,就需要循环遍历每个key都变成响应式,但是如果每个object[key]当中还含有多个key时,这时候就需要递归处理,(需要借鉴一下Vue的思路)
function defineReactive(obj, keyName, value) {
if (arguments.length === 2) {
value = obj[keyName]
}
// 调用观察函数
observe(value)
Object.defineProperty(obj, keyName, {
get() {
return value
},
set(newValue) {
if (val === newValue) {
return
}
value = newValue
// 定义子项
observe(newValue)
console.log("改变a属性为", newValue)
},
enumerable: true,
})
}
function def(obj, key, value, enumerable) {
Object.defineProperty(obj, key, {
value,
enumerable,
writable: true,
})
}
class Observer {
constructor(obj) {
// 将observe创建出来的实例,添加到当前层对象当中
def(obj, "__ob__", this)
// 遍历当前对象
this.walk(obj)
}
walk(obj) {
// obj一定是个object,因为observe做了判断
console.log(obj, "walk")
// 定义当前层为响应式
for (let key in obj) {
defineReactive(obj, key)
}
}
}
// 创建一个Observer实例,并且能够进入Observer的初始化
function observe(value) {
if (typeof value !== "object") {
return
}
let ob
if (typeof value.__ob__ !== "undefined") {
ob = value.__ob__
} else {
ob = new Observer(value)
}
return ob
}
处理数组的响应式
import def from "./def"
const arrayPrototype = Array.prototype
// 以Array.prototype为原型创建arrayMethods对象
export const arrayMthods = Object.create(arrayPrototype)
console.log(arrayMthods)
const methodsName = ["push", "pop", "shift","splice","unshift","revert","sort"]
methodsName.forEach(method => {
const original = arrayPrototype[method]
def(
arrayMthods,
method,
function () {
// 把这个数组身上的__ob__取出来
const ob = this.__ob__
let inserted = []
switch (method) {
case "push":
case "unshift":
inserted = arguments
break
case "splice" :
inserted = [...arguments].slice(2)
break
}
if (inserted.length > 0) {
ob.observeArray(inserted)
}
return original.apply(this, arguments)
},
false
)
})
Wacher类
import Dep from "./Dep"
function parsePath(exp) {
const arr = exp.split(".")
return obj => {
for (let i = 0; i < arr.length; i++) {
obj = obj[i]
}
return obj
}
}
export default class Watcher {
constructor(target, exp, callback) {
this.target = target
// "exp a.b.c.d"
this.getter = parsePath(exp)
this.callback = callback
this.value = this.get()
}
update() {}
get() {
// 依赖收集
Dep.target = this
const obj = this.target
let value
try {
value = this.getter(obj)
} finally {
Dep.target = null
}
return value
}
run() {
this.getAndInvoke(this.callback)
}
getAndInvoke(cb) {
const value = this.get()
if (value !== this.value || typeof value == "object") {
const oldValue = this.value
this.value = value
cb.call(this.target, value, oldValue)
}
}
}