以下为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({})
})
在执行上述测试用例时,会发现所有的测试都通过了。