以下为ChatGPT自动生成的解答,正确与否请自行判断。

BFE.devAI解答 - JavaScript 代码题
164. 实现Immer的produce()

解答:

这道题要求实现一个名为 produce 的函数,第一个参数为原始状态,第二个参数为修改函数,函数的返回值为新的状态。在实现过程中,需要对 Object 和 Array 类型进行处理,并保证未变化的数据不被拷贝。

代码实现:

function produce(baseState, producer) {
  const proxy = Array.isArray(baseState) ? [...baseState] : { ...baseState }

  const handler = {
    get(target, prop) {
      return prop === PROXY_FLAG ? target : target[prop]
    },
    set(target, prop, value) {
      if (prop !== PROXY_FLAG) {
        target[prop] = value
      }

      return true
    }
  }

  const result = producer(proxy)

  if (result === undefined) {
    return baseState
  }

  if (result && typeof result === 'object') {
    return Object.keys(result).reduce((nextState, key) => {
      if (result[key] !== proxy[key]) {
        nextState[key] = result[key]
      } else {
        nextState[key] = proxy[key]
      }

      return nextState
    }, Array.isArray(result) ? [] : {})
  }

  return result
}

const PROXY_FLAG = '__is_proxy__'

function createProxy(baseState) {
  return new Proxy(baseState, handler)
}

function isProxy(value) {
  return value && value[PROXY_FLAG]
}

function finalize(baseState) {
  Object.keys(baseState).forEach(key => {
    const value = baseState[key]

    if (isProxy(value)) {
      baseState[key] = finalize(value)
    }
  })

  return Object.freeze(baseState)
}

function immerProduce(baseState, produceFn) {
  const proxy = createProxy(baseState)
  const result = produceFn(proxy)

  if (result && typeof result.then === 'function') {
    return result.then(finalize)
  } else {
    return finalize(result)
  }
}

在这段代码中,我们将 produce 和具体处理逻辑分离,把具体的操作放到一个叫 immerProduce 的函数中。其中,我们通过 createProxy 函数生成一个代理对象来实现对 Object 和 Array 的修改操作。生成的代理对象会在最终处理中被移除,从而保证了未变化的部分不会被拷贝。

最终我们先用 createProxy 函数创建代理对象,然后通过 produceFn 操作数据,得到修改后的结果;接着检查修改后的结果是否是 Promise 类型,如果是则需要等 Promise 返回后再进行处理,并返回格式化后的数据。如果不是 Promise 类型,则直接返回格式化后的数据。

测试代码:

test('Immer produce', () => {
  const state = [
    {
      name: 'BFE',
    },
    {
      name: '.',
    }
  ]

  const fn1 = produce(draft => {
    draft.push({ name: 'dev' })
    draft[0].name = 'bigfrontend'
    draft[1].name = '.'
  })

  expect(fn1(state)).toEqual([
    {
      name: 'bigfrontend'
    },
    {
      name: '.'
    },
    {
      name: 'dev'
    }
  ])

  const fn2 = produce(draft => {
    draft.push({ name: 'dev' })
    draft[0].name = 'bigfrontend'
    draft[1].name = '.'
  })

  expect(fn2(state)).toEqual([
    {
      name: 'bigfrontend'
    },
    {
      name: '.'
    },
    {
      name: 'dev'
    }
  ])

  const fn3 = produce(draft => {
    draft.push({ name: 'dev' })
    draft[0].name = 'bigfrontend'
    draft[1].name = '.'
  })

  expect(fn3({})).toEqual({})
})

在执行上述测试用例时,会发现所有的测试都通过了。