isReactive
测试用例 - 在reactive的基础上
describe('reactive', () => {
it('happy path', () => {
const original = { foo: 1 }
const observed = reactive(original)
expect(observed).not.toBe(original)
expect(observed.foo).toBe(1)
// 检查observed和original是不是响应式对象和原始对象
// isReactive(observed) === true
expect(isReactive(observed)).toBe(true)
// isReactive(original) === false
expect(isReactive(original)).toBe(false)
})
})
功能描述
用来检查传入的对象是不是reactive,是则返回true,否则返回false
实现
先考虑下该怎么去实现,上一篇将代码进行了重构,抽出了createGetter方法,在调用createGetter的时候传入了一个参数用来标识这个对象是readonly还是reactive。可以根据这一点来检查是否是reactive对象。
所以呢检查创建这个getter时候传入的isReadonly的值就可以判断它是否是reactive对象。那么怎么获取这个isReadonly呢,前面说创建getter,这个getter在访问代理对象的时候会执行,那么就可以获取到这个isReadonly了。
如果不是代理对象呢?先实现上面的再说。
// 定义两个常量,表示访问的key值
const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly'
}
// 步骤一:根据上面描述中说的,访问一个属性,调用它的get方法,接着看步骤二
function isReactive (value) {
return value[ReactiveFlags.IS_REACTIVE]
}
// 更新getter
function createGetter (isReadonly = false) {
return function get (target, key) {
// 步骤二:调用它的get方法,就可以拿到外部函数的isReadonly
// 如果访问的key是 is_reactive,就说明调用了isReactive
if (key === ReactiveFlags.IS_REACTIVE) {
// 如果isReadonly是false,就说明它是一个reactive
return !isReadonly
}
const res = Reflect.get(target, key)
if (!isReadonly) {
track(target, key)
}
return res
}
}
上面留了一个问题,如果是普通对象呢
如果是普通对象就不会调用get方法,value[ReactiveFlags.IS_REACTIVE]的值就是undefined,给它进行两次取反就可以。如果是普通对象 !!undefined = false
// 更新isReactive
function isReactive (value) {
// value[ReactiveFlags.IS_REACTIVE] 如果value是一个原始对象,那么它不会去触发get方法,返回的就是undefined,所以取反两次转为布尔值
return !!value[ReactiveFlags.IS_REACTIVE]
}
isReadonly
测试用例
describe('readonly', () => {
it('happy path', () => {
const original = { foo: 1, bar: { baz: 2 } }
const wrapped: any = readonly(original)
expect(wrapped).not.toBe(original)
expect(wrapped.foo).toBe(1)
// 在测试readonly的基础上
expect(isReadOnly(wrapped)).toBe(true)
expect(isReadOnly(original)).toBe(false)
})
it('warn then call set', () => {
console.warn = jest.fn()
const user = readonly({ age: 10 })
user.age = 11
expect(console.warn).toBeCalled()
})
})
功能描述
用来检查传入的对象是不是readonly,是则返回true,否则返回false
实现
与isReactive同理,所以不再过多描述
export function isReadOnly (value) {
return !!value[ReactiveFlags.IS_READONLY]
}
// 更新createGetter
function createGetter (isReadonly = false) {
return function get (target, key) {
// 如果访问的key是 is_readonly,就说明调用了isReadonly
if (key === ReactiveFlags.IS_READONLY) {
// 直接返回isReadonly就可以
return isReadonly
}
const res = Reflect.get(target, key)
if (!isReadonly) {
track(target, key)
}
return res
}
}
最终代码
// 不了解ts的同学们,将它们当作js中的两个常量即可
const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly'
}
function isReactive (value) {
return !!value[ReactiveFlags.IS_REACTIVE]
}
function isReadOnly (value) {
return !!value[ReactiveFlags.IS_READONLY]
}
function createGetter (isReadonly = false) {
return function get (target, key) {
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
}
const res = Reflect.get(target, key)
if (!isReadonly) {
track(target, key)
}
return res
}
}