set和map两种原型属性和方法
相同的方法
- size
- delete
- has
- clear
- keys
- values
- entries
- forEach
不同的方法
1、set
- add
2、map
- set
- get
1、如何代理Set和Map
需要实现下面的代码
const proxy = new Proxy (new Map([['key', 1]]))
effect(() => {
console.log(proxy.get('key'))
})
proxy.set('key', 2);
根据Array的经验来说, 一般会跟size相关
const s = new Set([1,2,3])
const p = new Proxy(s, {})
p.size
上面代码会报错, 因为size是一个访问器属性, 即{get size() { return xxx}} , 但是我们当面在get中设置了代理器为this对象
Reflect.get(target, key, recevier)
所以更改代理方法get
const s = new Set([1, 2, 3])
const p = new Proxy(s, {
get(target, key, receiver) {
if (key === 'size') {
return Reflect.get(target, key, target)
}
// 读取其他属性的默认行为
return Reflect.get(target, key, receiver)
}
})
但是当测试delete方法的时候
p.delete(1)
// Uncaught TypeError: Method Set.prototype.delete called on incompatible receiver #<Set> at Proxy.delete
p.detele中的this是原生对象s, 但是p.detele(1)的this是代理对象
原因:
let obj = {foo: 1, getFoo: function () {console.log(this)} }
obj.getFoo() // getFoo的this指向的就是obj, {foo: 1, getFoo: ƒ}
let p = new Proxy(obj, {})
p.getFoo() // getFoo的this指向的就是代理对象p, Proxy {foo: 1, getFoo: ƒ}
//为什么Reflect设置了receiver为target不起作用, 因为receiver只对get方法的this起作用, 如下面示例
let setObj = {
value: 1,
get size() {
return this.value;
},
}
Reflect.get(setObj, "size", {value: 2}) // 2;
需要把Reflect方法更改为bind
function isSetMap(target) {
let idx = ["Set", "Map"].findIndex(type => Object.prototype.toString.call(target) === `[object ${type}]`)
return idx !== -1;
}
get(target, key, receiver) {
if(key === "raw") return target;
// 如果操作目标是set或者map
if(isSetMap(target)) {
if(key === "size") return Reflect.get(target, key, target);
return target[key].bind(target);
}
// 如果操作目标是数据, 并key存在arrayInstrumentations上, 那么返回定义在arrayInstrumentations的中
if(Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
// 为什么这里就直接返回, 因为不需要跟踪数组的方法
return Reflect.get(arrayInstrumentations, key, receiver);
}
if(!isReadOnly && typeof key !== "symbol") {
track(target, key);
}
let res = Reflect.get(target, key, receiver);
if(isShallow) return res;
if(typeof res === "object" && res !== null) {
return isReadOnly ? readonly(res) : reactive(res);
}
return res;
}
2、建立响应关系
类似数组一样, 新增或者删除元素会影响set和map的size, 所以需要对size进行跟踪
if(isSetMap(target)) {
if(key === "size") {
track(target, ITERATE_KEY)
return Reflect.get(target, key, target)
}
return mutableInstrumentations[key];
}
需要在调用方法的时候, 触发副作用函数
const mutableInstrumentations = {
add(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.add(key);
if(!hasKey) {
trigger(target, key, "ADD");
}
return res;
},
delete(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.delete(key);
if(!hasKey) {
trigger(target, key, "DELETE");
}
return res;
}
}
new Proxy(data, {
// 对应obj.key
get(target, key, receiver) {
if(key === "raw") return target;
// 如果操作目标是set或者map
if(isSetMap(target)) {
if(key === "size") {
track(target, ITERATE_KEY)
return Reflect.get(target, key, target)
}
return mutableInstrumentations[key];
}
if(Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
return Reflect.get(arrayInstrumentations, key, receiver);
}
if(!isReadOnly && typeof key !== "symbol") {
track(target, key);
}
let res = Reflect.get(target, key, receiver);
if(isShallow) return res;
if(typeof res === "object" && res !== null) {
return isReadOnly ? readonly(res) : reactive(res);
}
return res;
},
.....
}
3、避免污染原始数据
按照set和map的方法对比可知, set拥有add方法, map拥有set和get方法, 同样需要实现
const mutableInstrumentations = {
add(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.add(key);
if(!hasKey) {
trigger(target, key, "ADD");
}
return res;
},
delete(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.delete(key);
if(!hasKey) {
trigger(target, key, "DELETE");
}
return res;
},
set(key, newVal) {
const target = this.raw;
const hasKey = target.has(key);
const oldVal = target.get(key);
let res = target.set(key, newVal);
if(!hasKey) {
trigger(target, key, "ADD");
} else if(newVal !== oldVal || (newVal === newVal && oldVal === oldVal)) { // 需要增加值的判断
trigger(target, key, "SET");
}
return res;
},
get(key) {
const target = this.raw;
let hasKey = target.has(key);
track(target, key)
if(hasKey) {
const res = target.get(key);
return typeof res === "object" && res !== null ? reactive(res) : res;
}
}
}
污染表现: 通过原始对象访问内部键值能触发响应, 但是原始数据不应该具有响应式数据的能力
let m1 = new Map();
let p1 = reactive(m1);
let m2 = new Map();
let p2 = reactive(m2);
p1.set("p2", p2);
effect(() => {
console.log(m1.get("p2").size)
})
m1.get("p2").set("foo", 1)
问题代码
set(key, newVal) {
const target = this.raw;
const hasKey = target.has(key);
const oldVal = target.get(key);
let res = target.set(key, newVal); // newVal原样设置到了原始数据target上面
if(!hasKey) {
trigger(target, key, "ADD");
} else if(newVal !== oldVal || (newVal === newVal && oldVal === oldVal)) { // 需要增加值的判断
trigger(target, key, "SET");
}
return res;
},
总结: 把响应式数据设置到原始数据上的行为成为数据污染
解决
set(key, newVal) {
const target = this.raw;
const hasKey = target.has(key);
const oldVal = target.get(key);
const rawVal = newVal.raw || newVal;
let res = target.set(key, rawVal); // 获取原始数据
if(!hasKey) {
trigger(target, key, "ADD");
} else if(newVal !== oldVal || (newVal === newVal && oldVal === oldVal)) { // 需要增加值的判断
trigger(target, key, "SET");
}
return res;
},
4、处理forEach
遍历操作只会与键值对的数量有关, 因此任何修改Map对象的键值对的操作都应该触发副作用函数重新执行, 例如删除delete和add等, 因此应该让forEach被调用时候, 让副作用函数与ITERATE_KEY相关联
const mutableInstrumentations = {
forEach(callback) {
const target = this.raw;
track(target, ITERATE_KEY);
target.forEach(callback)
}
}
上面的代码还有一下的问题
1、forEach传递给回调函数的参数是非响应数据
forEach(callback) {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY);
// callback接收三个参数, 1、当前的value, 2、当前的key, 3、正在被遍历的map对象
target.forEach((value, key) => {
callback(wrap(value), wrap(key), this)
})
}
2、forEach方法接受第二个参数, 用来指定callback函数执行时的this值
forEach(callback, thisArg) {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY);
// callback接收三个参数, 1、当前的value, 2、当前的key, 3、正在被遍历的map对象
target.forEach((value, key) => {
callback.call(thisArg, wrap(value), wrap(key), this)
})
}
此外, forEach遍历不仅关心集合的键, 还关心集合的值, 所以除了ADD和DELETE操作外, SET操作也应该触发副作用函数重新执行, 下面是trigger函数的修改
function trigger(target, key, type, newVal) {
let depsMap = bucket.get(target);
if(!depsMap) return;
let effectsToRun = new Set();
let effects = depsMap.get(key);
effects && effects.forEach(effectFn => {
if(effectFn !== activeEffect){
effectsToRun.add(effectFn);
}
})
.....
let iterateEffects = depsMap.get(ITERATE_KEY);
if(type === "ADD" || type === "DELETE" || (type === "SET" && isSetMap(target) )) {
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
})
}
effectsToRun.forEach(effectFn => {
if(effectFn.options.scheduler){
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
5、迭代器方法
三个迭代器方法: 1、keys 2、values 3、entries
迭代器的内部实现都是通过[Symbol.iterator]实现
由于代理的原理, 同样[Symbol.iterator]需要放在mutableInstrumentations中
[Symbol.iterator]() {
const target = this.raw;
const itr = target[Symbol.iterator]();
return itr; // 返回一个对象, 内部有next方法, next方法返回value和done
}
上面的代码存在跟forEach一样的问题, 迭代的值没有被代理, 是非响应数据, 需要修改成下面代码
[Symbol.iterator]() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
const itr = target[Symbol.iterator]();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
}
}
}
}
以上只是解决了迭代值为非响应数据, 还没有对迭代器进行响应关系绑定
[Symbol.iterator]() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY); // 调用track函数建立响应关系
const itr = target[Symbol.iterator]();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
}
}
}
}
通过下面代码测试, 可以发现结合的响应式数据功能已经相对完整,下面是测试代码
const map = reactive(new Map([["bar",2]]))
effect(() => {
for(const [key, value] of map ){
console.log(key, value)
}
})
map.set("foo", 1)
// bar 2
// bar 2
// foo 1
下面是set和map的等价关系, 通过[Symbol.iterator]可以实现对应的迭代器方法
const m = new Map()
m[Symbol.iterator] === m.entries // true
const s = new Set()
s[Symbol.iterator] === s.values // true
function iterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY)
const itr = target[Symbol.iterator]();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
}
}
}
}
const mutableInstrumentations = {
....
[Symbol.iterator]: iterationMethod,
entries: iterationMethod,
}
但尝试用for...of进行迭代会报错
const map = reactive(new Map([["bar",2]]))
effect(() => {
for(const [key, value] of map.entries() ){
console.log(key, value)
}
})
map.set("foo", 1)
// map.entities is not a function or its return value is not iterable
// p.entries的返回值不是一个可迭代对象, 一个可迭代对象应该具有Symbol.iterator方法
- 可迭代协议指的是一个对象实现了Symbol.interator方法
- 迭代器协议指的是一个实现了next方法
一个对象可同时实现可迭代协议和迭代器协议
const obj = {
next() {}
[Symbol.iterator]() {
return this;
}
}
同样处理iterationMethod方法
function iterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY)
const itr = target[Symbol.iterator]();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
}
},
[Symbol.iterator]() {
return this
}
}
}
6、values和keys方法
跟entries类似, 但是存在不同
1、values
- 关于迭代器对象, entries使用target[Symbol.iterator]获取, 而values使用target.values获取
- next方法的值, entries处理的是键值对, 而values处理的是值
function valueIterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY)
const itr = target.values();
return {
next() {
const {value, done} = itr.next();
return {
value: wrap(value),
done
}
},
[Symbol.iterator]() {
return this
}
}
}
2、keys
keys与values的差别有在, 前面在trigger有加过判断 type === "ADD" || type === "DELETE" || (type === "SET" && isSetMap(target) ), 当设置值的时候, 也会触发副作用函数, 但是这个keys没有影响, keys只对"ADD"和"DELETE"需要处理
const ITERATE_KEY = Symbol();
function keyIterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, MAP_KEY_ITERATE_KEY) // 重新定义个key值
const itr = target.keys();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value:wrap(value),
done
}
},
[Symbol.iterator]() {
return this
}
}
}
trigger函数中需要加上对应的判断
if((type === "ADD" || type === "DELETE") && isSetMap(target)) {
let iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY);
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
})
}
整体代码
const ITERATE_KEY = Symbol(); // 唯一个的key, 只需要建一个, 因为每个对象下面只会挂载一个ITERATE_KEY, trigger的时间, 会先寻找对象, 后再寻找ITERATE_KEY。 可以理解对象不同, 但ITERATE_KEY相同
const MAP_KEY_ITERATE_KEY = Symbol();
let activeEffect // 当前副作用函数
let effectStack = []; // 副作用函数栈
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
// 当调用effect注册副作用函数时,将副作用函数赋值给activeEffect
activeEffect = effectFn;
// 调用副作用函数之前将副作用函数压入栈
effectStack.push(effectFn);
let res = fn()
// 在当前副作用函数执行完成之后, 将当前副作用函弹出栈,并将activeEffect还原为之前的值
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
}
// 将options挂载到effectFn上
effectFn.options = options;
// 用来所有与该副作用函数相关的依赖集合
effectFn.deps = [];
if(!options.lazy){
effectFn();
}
return effectFn;
}
// 副作用函数依赖合集的删除
function cleanup(effectFn) {
for(let i = 0; i < effectFn.deps.length; i++){
let deps = effectFn.deps[i];
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// 桶,储存代理对象属性的相关事件
const bucket = new WeakMap();
// 跟踪函数
function track(target, key) {
if(!activeEffect || !shouldTrack) return;
let depsMap = bucket.get(target);
if(!depsMap) bucket.set(target, depsMap = new Map());
let deps = depsMap.get(key);
if(!deps) depsMap.set(key, deps = new Set())
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
// 触发函数
function trigger(target, key, type, newVal) {
let depsMap = bucket.get(target);
if(!depsMap) return;
let effectsToRun = new Set();
let effects = depsMap.get(key);
effects && effects.forEach(effectFn => {
if(effectFn !== activeEffect){
effectsToRun.add(effectFn);
}
})
if(Array.isArray(target) && key === "length") {
depsMap.forEach((effects, key) => {
if(key >= newVal) {
effects && effects.forEach(effectFn => {
if(effectFn !== activeEffect){
effectsToRun.add(effectFn);
}
})
}
})
}
if(Array.isArray(target) && type === "ADD") {
let lengthEffects = depsMap.get("length");
lengthEffects && lengthEffects.forEach(effectFn => {
if(effectFn !== activeEffect){
effectsToRun.add(effectFn);
}
})
}
if(type === "ADD" || type === "DELETE" || (type === "SET" && isSetMap(target) )) {
let iterateEffects = depsMap.get(ITERATE_KEY);
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
})
}
if((type === "ADD" || type === "DELETE") && isSetMap(target)) {
let iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY);
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
})
}
effectsToRun.forEach(effectFn => {
if(effectFn.options.scheduler){
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function computed(getter) {
let effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
trigger(obj, "value")
}
});
let obj;
let value;
let dirty = true;
obj = {
get value() {
if(dirty) {
track(obj, "value");
value = effectFn();
dirty = false;
}
return value
}
}
return obj;
}
function traverse(value, seen = new Set()) {
if(typeof value !== "object" || value === null || seen.has(value)) return value;
// 将数据添加到seen中, 代表遍历读取过了,避免循环引用引起的死循环
seen.add(value)
// 这里只考虑到了对象, 没有考虑到数组等结构体
for(let key in value) {
traverse(value[key], seen)
}
return value;
}
function watch(source, cb, options = {}) {
let getter;
if(typeof source === "function") {
getter = source;
} else {
// 如果改为getter = traverse(source), 那么一开始就已经遍历obj下面的所有属性, 返回一个obj对象,
// 后面effect中调用() => getter的时候, 访问的只是obj对象, 所有当对obj的对象进行设置的时候, 不起作用
getter = () => traverse(source);
}
let newValue, oldValue;
let cleanUp;
function onInvalidate(cb) {
cleanUp = cb;
}
const job = () => {
newValue = effectFn();
if(cleanUp) {
cleanUp();
}
cb(newValue, oldValue, onInvalidate)
oldValue = newValue;
}
let effectFn = effect(() => getter(), {
lazy: true,
scheduler() {
if(options.flush === "post") {
Promise.resolve().then(() => {
job();
})
} else {
job();
}
}
})
if(options.immediate) {
job();
} else {
oldValue = effectFn();
}
}
const arrayInstrumentations = {};
["includes", "indexOf", "lastIndexOf"].forEach(method => {
arrayInstrumentations[method] = function(...args) {
let originMethod = Array.prototype[method];
let res = originMethod.apply(this, args) // 这里的this是虚拟对象, 因为方法是通过Reflect.get(.., receiver)调用的
if(res === false || res === -1) {
res = originMethod.apply(this.raw, args) // 如果没找到, 则通过this.raw拿到原始数组再去比对
}
return res;
}
});
let shouldTrack = true;
["push", "pop", "push", "shift", "unshift"].forEach(method => {
let originMethod = Array.prototype[method];
arrayInstrumentations[method] = function(...args) {
shouldTrack = false;
let res = originMethod.apply(this, args)
shouldTrack = true;
return res;
}
})
function isSetMap(target) {
let idx = ["Set", "Map"].findIndex(type => Object.prototype.toString.call(target) === `[object ${type}]`)
return idx !== -1;
}
function iterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY)
const itr = target[Symbol.iterator]();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: value ? [wrap(value[0]), wrap(value[1])] : value,
done
}
},
[Symbol.iterator]() {
return this
}
}
}
function valueIterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY)
const itr = target.values();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value: wrap(value),
done
}
},
[Symbol.iterator]() {
return this
}
}
}
function keyIterationMethod() {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, MAP_KEY_ITERATE_KEY)
const itr = target.keys();
// 返回自定义迭代器
return {
next() {
const {value, done} = itr.next();
return {
value:wrap(value),
done
}
},
[Symbol.iterator]() {
return this
}
}
}
const mutableInstrumentations = {
add(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.add(key);
if(!hasKey) {
trigger(target, key, "ADD");
}
return res;
},
delete(key) {
const target = this.raw;
const hasKey = target.has(key);
let res = target.delete(key);
if(!hasKey) {
trigger(target, key, "DELETE");
}
return res;
},
set(key, newVal) {
const target = this.raw;
const hasKey = target.has(key);
const oldVal = target.get(key);
const rawVal = newVal.raw || newVal;
let res = target.set(key, rawVal); // 获取原始数据
if(!hasKey) {
trigger(target, key, "ADD");
} else if(newVal !== oldVal || (newVal === newVal && oldVal === oldVal)) { // 需要增加值的判断
trigger(target, key, "SET");
}
return res;
},
get(key) {
const target = this.raw;
let hasKey = target.has(key);
track(target, key)
if(hasKey) {
const res = target.get(key);
return typeof res === "object" && res !== null ? reactive(res) : res;
}
},
forEach(callback, thisArg) {
const wrap = (val) => typeof val === "object" ? reactive(val) : val;
const target = this.raw;
track(target, ITERATE_KEY);
// callback接收三个参数, 1、当前的value, 2、当前的key, 3、正在被遍历的map对象
target.forEach((value, key) => {
callback.call(thisArg, wrap(value), wrap(key), this)
})
},
[Symbol.iterator]: iterationMethod,
entries: iterationMethod,
values: valueIterationMethod,
keys: keyIterationMethod,
}
function createReactive(data, isShallow = false, isReadOnly = false) {
return new Proxy(data, {
// 对应obj.key
get(target, key, receiver) {
if(key === "raw") return target;
// 如果操作目标是set或者map
if(isSetMap(target)) {
if(key === "size") {
track(target, ITERATE_KEY)
return Reflect.get(target, key, target)
}
return mutableInstrumentations[key];
}
// 如果操作目标是数据, 并key存在arrayInstrumentations上, 那么返回定义在arrayInstrumentations的中
if(Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
// 为什么这里就直接返回, 因为不需要跟踪数组的方法
return Reflect.get(arrayInstrumentations, key, receiver);
}
if(!isReadOnly && typeof key !== "symbol") {
track(target, key);
}
let res = Reflect.get(target, key, receiver);
if(isShallow) return res;
if(typeof res === "object" && res !== null) {
return isReadOnly ? readonly(res) : reactive(res);
}
return res;
},
// 对应obj.key = 11
set(target, key, newVal, receiver) {
if(isReadOnly) {
console.warn(`属性${key}是只读的`);
return true;
}
let oldValue = target[key];
let type = Array.isArray(target) ? Number(key) < target.length ? "SET" : "ADD" : Object.prototype.hasOwnProperty.call(target, key) ? "SET": "ADD";
let res = Reflect.set(target, key, newVal, receiver)
if(target === receiver.raw){
if(oldValue !== newVal && (oldValue === oldValue || newVal === newVal)){
trigger(target, key, type, newVal);
}
}
return res
},
// 对应delete obj.key
deleteProperty(target, key) {
if(isReadOnly) {
console.warn(`属性${key}是只读的`);
return true;
}
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
const res = Reflect.deleteProperty(target, key);
if(hadKey && res) {
trigger(target, key, "DELETE");
}
},
// 对应key in obj
has(target, key) {
track(target, key)
return Reflect.has(target, key)
},
// 对应for(ley key in obj)
ownKeys(target) {
// const ITERATE_KEY = new Symbol()
// 因为for...in没有对应的key, 所以通过Symbol来创建唯一的key建立对应的响应关系
track(target, Array.isArray(target) ? "length" : ITERATE_KEY)
return Reflect.ownKeys(target)
}
});
}
let reactiveMap = new Map();
function reactive(data) {
let existionProxy = reactiveMap.get(data);
if(existionProxy) return existionProxy
let proxy = createReactive(data)
reactiveMap.set(data, proxy)
return proxy;
}
function shallowReactive(data) {
return createReactive(data, true)
}
function readonly(data) {
return createReactive(data, false, true);
}
function shallowReadonly(data){
return createReactive(data, true, true)
}