const data = { foo: 1 }const obj = new Proxy(data, {/*...*/})effect(() => obj.foo = obj.foo + 1)

此项操作会引起栈溢出:

Uncaught RangeError: Maximum call sack size exceeded

在此操作中, 会先读取obj.foo的值, 这会触发track操作, 将副作用函数入栈, 此时有加一并赋值, 此时会触发trigger操作, 将副作用函数出栈并执行, 在这种状况下, 该副作用函数还在执行中, 又开始下一次的执行, 导致有限递归调用本人导致栈溢出报错.

在这个操作中读取与设置的是同一个副作用函数activeEffect, 因而在trigger要触发时增加条件: 如果trigger触发的副作用函数与以后执行的副作用函数雷同, 则不触发执行:

function trigger (target, key) {    const depsMap = bucket.get(target)    if (!depsMap) return    const effects = depsMap.get(key)        const effectsToRun = new Set()    effects && effects.forEach(effectFn => {        if (effectFn !== activeEffect) {            effectsToRun.add(effectFn)        }    })    effectsToRun.forEach(effectFn => effectFn())}

目前为止响应式残缺代码

  // 贮存副作用函数的桶  const bucket = new WeakMap()    // 用于贮存被注册的副作用的函数  let activeEffect = undefined  // 副作用函数栈  const effectStack = []  function cleanup (effectFn) {    for (let itme of effectFn.deps) {      itme.delete(effectFn)    }    effectFn.deps.length = []  }    function effect (fn) {    const effectFn = () => {        cleanup(effectFn)                // 调用以后的副作用函数时, 赋值给 全局变量        activeEffect = effectFn        // 在调用副作用函数之前将该函数压入栈        effectStack.push(effectFn)                fn()                // 以后的副作用函数执行完结后, 出栈        effectStrack.pop()        // activeEffect 还原为之前的值        activeEffect = effectStack[effectStack.length - 1]    }        effectFn.desp = []    effectFn()  }    const data = {    text: 'hello world',    ok: true  }      const obj = new Proxy(data, {    // 拦挡读取操作    get (target, key) {      track(target, key)      // 返回属性值      return target[key]    },      // 拦挡设置操作    set (target, key, newVal) {      // 设置属性值      target[key] = newVal      trigger(target, key)    }  })    function track (target, key) {    // 没有 activeEffect, 间接 return    if (!activeEffect) return target[key]      // 依据 target 从'桶'中回去 depsMap, 它也是一个 Map 类型: key ---> effects    let depsMap = bucket.get(target)    // 如果 depsMap 不存在, 则新建一个 Map 并与 target 关联    if (!depsMap) bucket.set(target, (depsMap = new Map()))    // 再依据 key 从depsMap 中去的 deps, 它是一个 Set 类型    // 外面存贮所有与以后 key 相干的副作用函数: effects    let deps = depsMap.get(key)    // 如果 deps 不存在, 同样新建一个 Set 并与 key 关联0    if (!deps) depsMap.set(key, (deps = new Set()))    // 最初将以后激活的副作用函数增加到'桶'里    deps.add(activeEffect)  }    function trigger (target, key) {    // 依据 target 从'桶'中获得 depsMap, 它是 key --> effects    const depsMap = bucket.get(target)    if (!depsMap) return    // 依据 key 获得所有的副作用函数 effects    const effects = depsMap.get(key)      // 执行副作用函数    const effectsToRun = new Set()    effects && effects.forEach(effectFn => {        // 若触发执行的副作用函数与以后正在执行的副作用函数雷同, 则不触发执行        if (effectFn !== activeEffect) {            effectsToRun.add(effectFn)        }    })    effectsToRun.forEach(effectFn => effectFn())  }    effect(() => {    console.log('effect run');    document.body.innerText = obj.ok ? obj.text : 'not'  })    setTimeout(() => {    obj.ok = false  }, 2000)