Helper method

// Update the data
function patch(o, n){
    o.val = n.val
}
// Simulate dom node movement
function insertBefore(prev, node, container, step = 0){
    if(prev === null){
        remove(node, container)
        container.push(node) 
        return container
    }
    if(prev && prev.key === node.key) return container
    let idx = 0
    for (let i = 0; i < container.length; i++) {
        const n = container[i];
        if(n.key === node.key){
            [ node ] = container.splice(i, 1)
            break}}// console.log(prev, node, container)
    for (let i = 0; i < container.length; i++) {
        const n = container[i];
        if(prev && n.key === prev.key){
            idx = i + step
            break}}// console.log(idx)
    // console.log(container.slice(0, idx))
    // console.log(container.slice(idx))
    return [
        ...container.slice(0, idx), node, ... container.slice(idx) ] }// Delete an element method
function remove(node, container){
    for (let i = 0; i < container.length; i++) {
        const n = container[i];
        if(node && n.key === node.key){
            container.splice(i, 1)
            break}}return container
}
// Find the longest ascending subsequence
function lis(arr) {
    const p = arr.slice()
    const result = [0]
    let i
    let j
    let u
    let v
    let c
    const len = arr.length
    for (i = 0; i < len; i++) {
        const arrI = arr[i]
        if(arrI ! = =0) {
            j = result[result.length - 1]
            if (arr[j] < arrI) {
                p[i] = j
                result.push(i)
                continue
            }
            u = 0
            v = result.length - 1
            while (u < v) {
                c = ((u + v) / 2) | 0
                if (arr[result[c]] < arrI) {
                    u = c + 1
                } else {
                    v = c
                }
            }
            if (arrI < arr[result[u]]) {
            if (u > 0) {
                p[i] = result[u - 1]
            }
                result[u] = i
            }
        }
    }
    u = result.length
    v = result[u - 1]
    while (u-- > 0) {
        result[u] = v
        v = p[v]
    }
    return result
}
Copy the code

react

Algorithm process

  1. traversenextTake out thenextNodePosition,i
  2. inprevLook fornextNode.keyA consistent node, if not found, is created and inserted intonext[i - 1]after
  3. If the node is found, the location isj, update the node, and judgejIs less thanLastIdx (lastIdx = 0)If is not less than, thenlastIdx = j
  4. If less thanlastIdxInserts the node intonext[i - 1]after
  5. traversenextWhen finished, iterateprevIf the node is not therenextIs displayed, the node is deleted

Code implementation

// react
function diff1(prev, next){
    let container = prev.slice()
    let nextIndex = {}
    let prevIndex = {}
    let prevLength = prev.length
    let nextLength = next.length
    let lastIdx = 0
    // Generate a mapping table for easy lookup
    for(let i = 0; i < prevLength; i++) prevIndex[prev[i].key] = i
    for(let i = 0; i < nextLength; i++){
        // 1. Iterate next to get nextNode, position I
        let nextNode = next[i]
        nextIndex[nextNode.key] = i
        // 3. If a node is found and its position is j, update the node and determine whether j is less than lastIdx (lastIdx = 0). If not, then lastIdx = j
        let j = prevIndex[nextNode.key]
        if(j ! = =undefined){
            patch(prev[j], nextNode)
            // 4. If less than lastIdx, insert the node after next[i-1]
            if(j < lastIdx){
                container = insertBefore(next[i - 1], nextNode, container, 1)}else{
                lastIdx = j
            }
        }else{
            Nextnode.key = nextnode.key; next[i-1] = next[i-1]
            container = insertBefore(next[i - 1], nextNode, container, 1)}}// 5. After traversing next, traverse prev. If the node is not in next, delete the node
    for(let p of prev){
        if(nextIndex[p.key] === undefined){
            remove(p, container)
        }
    }
    return container
}
Copy the code

Execute the process

The first line represents the simulated Prev array (a copy), with dots representing the moved nodes and dotted lines representing the updated nodes

Line 2 represents the old prev array, with green indicating that it was operated on and red indicating the value currently traversed

Line 3 represents the new next array

Follow the React Diff process to simulate the next steps

Prev traversal generates the prevIndex mapping table between key and index

Select * from next where I = 0 and lastIdx = 0; select * from next where I = 0 and lastIdx = 0

I = 1 lastIdx = 1 [prevIndex[key] = 0 j = 0 j = 0

Select * from ‘prevIndex’ where ‘lastIdx’ = 1; select * from ‘prevIndex’ where ‘prevIndex’ = 1

I = 3 lastIdx = 3 [prevIndex[key] = 2 j = 2 j = 3 [prevIndex[key] = 3

I = 4 lastIdx = 3 I = 4 lastIdx = 3 I = 4 lastIdx = 3 I = 4 lastIdx = 3 I = 4 lastIdx = 3

After the traversal is complete, we need to traverse prev again and find out that f node does not exist in the new node array through nextIndex. We need to delete f node

vue2

Algorithm process

  1. Declare four variablesprevStart.prevEnd.nextStart.nextEndTake out theprevStartNode.prevEndNode.nextStartNode.nextEndNode
  2. Loop, the condition is not satisfiedprevStart <= prevEnd && nextStart <= nextEndJump out of the loop
  3. prevStartNodeprevEndNodeDoes it exist? Does it not existprevStart++.prevEnd--To return to 2
  4. prevStartNode.key == nextStartNode.key, if equal, update the node,prevStart++ nextStart++To return to 2
  5. prevEndNode.key == nextEndNode.key, if equal, update the node,prevEnd-- nextEnd--To return to 2
  6. prevStartNode.key == nextEndNode.key, is equal to update the node, willprevStartNodeInserted into theprevEndNode.nextBefore,prevStart++ nextEnd--To return to 2
  7. prevEndNode.key == nextStartNode.key, is equal to update the node, willprevEndNodeInserted into theprevStartNodeBefore,prevEnd-- nextStart++To return to 2
  8. If all of them are not equal, look upprevTo see if there is a relationship withnextStartNode.keyThe same node, if it is updated, if it is not, a new node is created and inserted into theprevStartNodeBefore,NextStart++, prev [j].Mark it as done. Go back to 2
  9. After the loop ends ifprevEnd < prevStartProve that there are new nodes not processed fromnextStartStart inserting nodes untilnewEnd, each time the node is inserted innext[newEnd + 1]before
  10. ifnextEnd < newStartProve that a node has been removed, unprocessed, fromprevStartStarts removing nodes untilprevEnd

Code implementation

// vue2
function diff2(prev, next){
    let container = prev.slice()
    // 1. Declare four variables prevStart, prevEnd, nextStart, and nextEnd to take out prevStartNode, prevEndNode, nextStartNode, and nextEndNode
    let prevStart = 0
    let nextStart = 0
    let prevEnd = prev.length - 1
    let nextEnd = next.length - 1
    let prevStartNode = prev[prevStart]
    let prevEndNode = prev[prevEnd]
    let nextStartNode = next[nextStart]
    let nextEndNode = next[nextEnd]
    let prevIndex = {}
    for(let i = 0; i < prev.length; i++) prevIndex[prev[i].key] = i
    // 2. Loop, condition does not meet prevStart <= prevEnd && nextStart <= nextEnd jump loop
    while(prevStart <= prevEnd && nextStart <= nextEnd){
        // 3. PrevStartNode or prevEndNode does not exist, prevStart++, prevEnd--, back to 2
        if(! prevStartNode){ prevStartNode = prev[++prevStart] }else if(! prevEndNode){ prevEndNode = prev[--prevEnd] }else if(prevStartNode.key === nextStartNode.key){
            // 4. Prevstartnode. key == nextstartnode. key, equal to update node, prevStart++ nextStart++, back to 2
            patch(prevStartNode, nextStartNode)
            prevStartNode = prev[++prevStart]
            nextStartNode = next[++nextStart]
        }else if(prevEndNode.key === nextEndNode.key){
            // 5. Prevendnode. key == NexTendNode. key, equal to update node, prevEnd-- nextEnd --, return to 2
            patch(prevEndNode, nextEndNode)
            prevEndNode = prev[--prevEnd]
            nextEndNode = next[--nextEnd]
        }else if(prevStartNode.key === nextEndNode.key){
            // 6.prevstartnode. key == nextendnode. key, equals to update node, insert prevStartNode before prevendnode. next, prevStart++ nextEnd--, return to 2
            patch(prevStartNode, nextEndNode)
            container = insertBefore(prevEndNode, prevStartNode, container, 1)
            prevStartNode = prev[++prevStart]
            nextEndNode = next[--nextEnd]
        }else if(prevEndNode.key === nextStartNode.key){
            // 7. Prevendnode. key == nextstartnode. key, update node, insert prevEndNode before prevStartNode, prevEnd-- nextStart++, back to 2
            patch(prevEndNode, nextStartNode)
            container = insertBefore(prevStartNode, prevEndNode, container)
            prevEndNode = prev[--prevEnd]
            nextStartNode = next[++nextStart]
        }else{
            NextStartNode ++, prev[J], prev[J], prev[J], prev[J], prev[J] Back to 2
            let j = prevIndex[nextStartNode.key]
            if(j ! = =undefined){
                patch(prev[j], nextStartNode)
                prev[j] = undefined
            }
            container = insertBefore(prevStartNode, nextStartNode, container)
            nextStartNode = next[++nextStart]
        }
    }
    if(prevEnd < prevStart){
        // 9. If prevEnd < prevStart proves that a new node has not been processed after the end of the loop, insert the node from nextStart until newEnd, each time before next[newEnd + 1]
        let ref = next[nextEnd + 1] | |null
        while(nextStart <= nextEnd){
            container = insertBefore(ref, next[nextStart++], container)
        }
    }else if(nextEnd < nextStart){
        // 10. If nextEnd < newStart proves that a node has been removed and has not been processed, remove the node from prevStart until prevEnd
        while(prevStart <= prevEnd){
            remove(prev[prevStart++], container)
        }
    }
    return container
}
Copy the code

Execute the process

Initialize prevIndex with prevStart = 0, prevEnd = 4, nextStart = 0, nextEnd = 4

By comparing prevStart with nextStart, prevEnd with nextEnd, prevStart with nextEnd, prevEnd with nextStart, Prev [J] = prev[J] = prev[J] = prev[J] = prev[J] = prev[J] NextStartNode = next[++nextStart]

PrevStart = 0, prevEnd = 4, nextStart = 1, nextEnd = 4

Key === nextstartnode. key update prevStartNode and execute prevStart++ nextStart++

PrevStartNode is a previously processed node, so skip prevStart++

PrevStart = 2, prevEnd = 4, nextStart = 2, nextEnd = 4

By comparing prevStart with nextStart, prevEnd with nextEnd, prevStart with nextEnd, prevEnd with nextStart, Prev [J] = prev[J] = prev[J] = prev[J] = nextStartNode NextStartNode = next[++nextStart]

PrevStart = 2, prevEnd = 4, nextStart = 3, nextEnd = 4

Key === nextstartnode. key update prevStartNode and execute prevStart++ nextStart++

PrevStartNode is a previously processed node, so skip prevStart++

PrevStart = 4, prevEnd = 4, nextStart = 4, nextEnd = 4

Compare prevStart with nextStart, prevEnd with nextEnd, prevStart with nextEnd, prevEnd with nextStart, and prevEnd with nextStart. NextStartNode = next[++nextStart]

PrevStart = 4, prevEnd = 4, nextStart = 5, nextEnd = 4

If prevStart > prevEnd does not exist, nextStart > nextEnd does not exist. If prevStart <= prevEnd does not exist, nextStart > nextEnd does not exist

Looking at a group consolidation

PrevStart = 0, prevEnd = 3, nextStart = 0, nextEnd = 4

PrevStartNode is equal to nextStartNode. After the update, move the pointer

Compare prevEndNode to nextEndNode, update the node, and move the node before prevStartNode

PrevStart = 1, prevEnd = 2, nextStart = 2, nextEnd = 4

We find that the prevEndNode and nextStartNode are equal, and the pointer is moved after updating the node

PrevStart = 1, prevEnd = 1, nextStart = 2, nextEnd = 3

We find that the prevStartNode and nextStartNode are equal, and the pointer is moved after updating the node

(prevStart = 2, prevEnd = 1, nextStart = 3, nextEnd = 3) The loop inserts the new node before next[nextEnd + 1]

vue3

Algorithm process

  1. jIndicates the position that is currently matched. The initial value is 0
  2. fromjStart matching the same element ifprev[j].key == next[j].keyAnd the updatedj++ifj > prevEnd || j > nextEndJump out ahead of time
  3. J stops at the first different point after matching the same element before
  4. fromprevEndnextEndStart looking for the same suffix from the back, after the updateprevEnd-- nextEnd--ifj > prevEnd || j > nextEndJump out ahead of time
  5. Now that we have updated the same prefix and suffix, we need to see where j is
  6. ifj > prevEnd && j <= nextEnd,next[j , nextEnd]Add elements directly innext[nextEnd + 1]Insert the new element before
  7. ifj > nextEnd && j <= prevEnd,prev[j , prevEnd]If the element is deleted, delete the redundant element directly
  8. If neither of the above two cases, it means in[j , prevEnd]The segment contains out-of-order nodes, and the length isnextLeft = nextEnd - j + 1
  9. Initializes an auxiliary arraysourceLength ofnextLeftThe default value is -1,patched = 0.move = false.pos = 0
  10. traversenext, fromjnextEndTo generate akey - iThe mapping tablekeyIndex
  11. traverseprev, fromjprevEnd
  12. ifpatchedIs greater thannextLeft, the same element fromprev, followed by the elements to be deleted, delete directly
  13. k = keyIndex[prev[i]]If thekNo, the node does not exist innext, directly delete
  14. next[k].key == prev[i].key, then update the node,patched++.source[k - j] = iIf thek < pos,prev[i]It has to be movedmove = true, otherwise,pos = k
  15. Judge after processingmoveWhether it is true
  16. If it’s not true,i = nextLeft - 1In reverse order, ifsource[i] == -1When,pos = j + iIn thenext[pos + 1]Add nodes in frontnext[pos]
  17. If true, an out-of-order/new situation occurs in this range
  18. Find the longest ascending subsequenceseq = lis(source) k = seq.length - 1
  19. i = nextLeft - 1In reverse order, ifsource[i] == -1When,pos = j + iIn thenext[pos + 1]Add nodes in frontnext[pos]
  20. ifi == seq[j], this node needs to be moved,pos = j + iIn thenext[pos + 1]Front insert nodenext[pos]
  21. In other cases,k--

Code implementation

// vue3
function diff3(prev, next){
    let container = prev.slice()
    // 1.j indicates the position of the current match. The initial value is 0
    let j = 0
    let prevEnd = prev.length - 1
    let nextEnd = next.length - 1
    let prevNode = prev[j]
    let nextNode = next[j]
    J / / 2. Start matching the same elements, if prev [j]. Key = = next [j]. The key, the updated j++ if j > prevEnd | | j > nextEnd jump out ahead of time
    while(prevNode && nextNode && prevNode.key === nextNode.key){
        patch(prevNode, nextNode)
        j++
        if(j > prevEnd || j > nextEnd) break
        prevNode = prev[j]
        nextNode = next[j]
    }
    // 3. J stops at the first difference after matching the previous identical elements
    prevNode = prev[prevEnd]
    nextNode = next[nextEnd]
    PrevEnd and nextEnd / / 4. Start from the back to find the same suffix, updated prevEnd - nextEnd - if j > prevEnd | | j > nextEnd jump out ahead of time
    while(prevNode && nextNode && prevNode.key === nextNode.key){
        patch(prevNode, nextNode)
        prevEnd--
        nextEnd--
        if(j > prevEnd || j > nextEnd) break
        prevNode = prev[prevEnd]
        nextNode = next[nextEnd]
    }
    // 5. At this point, the same prefix and suffix have been updated
    if(j > prevEnd && j <= nextEnd){
        // if j > prevEnd && j <= nextEnd, next inserts element before [j, nextEnd + 1]
        let ref = next[nextEnd + 1] | |null
        while(j <= nextEnd){
            container = insertBefore(ref, next[j++], container)
        }
    }else if(j > nextEnd && j <= prevEnd){
        // 7. If j > nextEnd && j <= prevEnd, prev is deleted from [j, prevEnd]
        while(j <= prevEnd){
            remove(prev[j++], container)
        }
    }else if(j <= nextEnd){
        // 8. If neither of the preceding two conditions exists, then there are nodes in the [j, prevEnd] segment with the length of nextLeft = nextEnd -j + 1
        let nextLeft = nextEnd - j + 1
        // 9. Initialize an auxiliary array source length nextLeft Default value -1, patched = 0, move = false, pos = 0
        let source = new Array(nextLeft).fill(-1)
        let pos = 0
        let patched = 0
        let move = false
        let keyIndex = {}
        // 10. Iterate next, from j to nextEnd, and generate a key-i mapping table keyIndex
        for(let i = j; i <= nextEnd; i++) keyIndex[next[i].key] = i
        // 11. Iterate over prev from j to prevEnd
        for (let i = j; i <= prevEnd; i++) {
            let prevNode = prev[i]
            // 12. If patched is larger than nextLeft, the same elements are retrieved from prev and the following elements are to be deleted. Delete the elements directly
            if(patched < nextLeft){
                let k = keyIndex[prevNode.key]
                K = keyIndex[prev[I]], if k does not exist, this node does not exist in next, delete it directly
                if(k ! = =undefined) {Next [k]. Key == prev[I]. Key, patched++, source[k - j] = I, if k < pos, prev[I] = true, otherwise pos = k
                    patch(prevNode, next[k])
                    source[k - j] = i
                    patched++
                    if(k < pos){
                        move = true
                    }else{
                        pos = k
                    }
                }else{
                    remove(prevNode, container)
                }
            }else{
                remove(prevNode, container)
            }
        }
        // 15. Check whether move is true
        if(move){
            // 17. If true, an out-of-order/new situation occurs in this range
            Seq = lis(source) j = seq.length-1
            let seq = lis(source)
            let k = seq.length - 1
            for(let i = nextLeft - 1; i >= 0; i--){
                if(source[i] === -1|| i ! == seq[k]){Next [pos + 1] next[pos + 1] next[pos + 1] next[pos + 1]
                    // 20. If I == seq[j], then we need to move the node, pos = j + I, insert next[pos] in prev before next[pos + 1] in prev
                    let pos = j + i
                    container = insertBefore(next[pos + 1] | |null, next[pos], container)
                }else{
                    // 21. Other information, J --
                    k--
                }
            }
        }else{
            Next [pos] next[pos + 1] next[pos + 1] next[pos + 1]
            for(let i = nextLeft - 1; i >= 0; i--){
                if(source[i] === -1) {let pos = j + i
                    container = insertBefore(next[pos + 1] | |null, next[pos], container)
                }
            }
        }
    }
    return container
}
Copy the code

Execute the process

NextEnd = 4 prevNode = prev[J] [J]

J = 1 prevNode = prev[j] nextNode = next[J]

Start comparison with the suffix node, and prevNode = prev[prevEnd] nextNode = next[nextEnd]. If the loop condition is met, start the loop

Update the suffix node until the loop condition is not met

<= prevEnd <= prevEnd && j <= nextEnd <= prevEnd && j <= prevEnd && j <= prevEnd Prove that the interval of [j, nextEnd] is out of order or new

NextLeft = nextEnd -j + 1 = 3 nextLeft = nextEnd -j + 1 = 3 nextLeft = nextEnd -j + 1 = 3 nextLeft = nextEnd -j + 1 = 3 Then iterate over the prev array from J to prevEnd to initialize pos = 0 patched = 0 move = false

When I = j = 1, pos = 0 patched = 0 nextLeft = 3 and patched < nextLeft look for keyIndex to see if there is a node subscript k = 2 in next, if found, update the node, Patched++, set source[k-j] = I, then source = [-1, 1, -1], judge k < pos, if less than, it needs to move, move = true, here obviously does not hold, so set pos = k = 2

When I = 2 and pos = 2 patched = 1, search for keyIndex to get k = 1, update node, patched++ command source[k -j] = I then source = [2, 1, -1], This node needs to be moved because k < pos, so move = true

Because we need to move, we need to determine the minimum number of moves through LIS (longest ascending subsequence), seq = lis(source) = [2], k = seq.length-1 = 1 and then we go forward from nextleft-1 until 0

When I = 2, source[I] = -1, -1 indicates that we have never processed this node, which must be a new node. Therefore, we need to add this node to the array. Since the source we obtained above is a relative coordinate, If the coordinate of the current node in next is pos = j + I = 3, it needs to be inserted in front of the next[pos + 1] node

Source [I]! Is equal to negative 1, then we judge I! == seq[k], this node is a node that needs to be moved, so we move this node before next[pos + 1].

I equals zero, none of these are true, so k–, and then the loop is over, and the match is done

Let’s go through the execution process one more time to understand

J = 0 prevEnd = 5 nextEnd = 5 j = 0 prevEnd = 5 The condition j > prevEnd && j <= nextEnd && j <= prevEnd is not met, which indicates that [j, nextEnd] = [0, 5] is out of order or new

KeyIndex, nextLeft = nextEnd -j + 1 = 5, source = [-1, -1, -1, -1, -1

When I = j = 0, patched = 0, pos = 0, move = false, use keyIndex to find the position of node A in next with subscript 1. After updating, record the position update variable. patched++, pos = 1, move = false, source[k – j] = source[1] = 0

When I = 1 patched = 1, pos = 1, move = false, keyIndex patched = 0 k < pos so node b needs to move, after update, record position, update variable, patched++, pos = 1, move = true, source[k – j] = source[0] = 1

When I = 2 patched = 2, pos = 1, move = true, use keyIndex to find k = 2 because k > pos so pos = k = 2, after update, record location, update variable, patched++, pos = 2, source[k – j] = source[2] = 2

When I = 3 patched = 3, pos = 2, use keyIndex to find k = 5 because k > pos so pos = k = 5, after update, record position, update variable, patched++, pos = 5, source[k – j] = source[5] = 3

When I = 4 patched = 4, pos = 5, use keyIndex to find k = 4 because k < pos so pos = 5, after update, record position, update variable, patched++, pos = 5, source[k – j] = source[4] = 4

When I = 5 patched = 5, pos = 5, use keyIndex to find k = 3 because k < pos so pos = 5, after update, record position, update variable, patched++, pos = 5, source[k – j] = source[3] = 5

At the end of the traversal, Seq = lis(source) = [0, 2, 5] K = seq.length-1 = 3 Starting from nextleft-1

When I = 5, source[5]! == -1 but I === seq[k] so I don’t do it, k–

When I = 4, source[4]! == -1 because I! == seq[k] so to move the node, pos = j + I = 4 next[pos] needs to insert before next[pos + 1]

When I = 3, source[4]! == -1 because I! == seq[k] so to move the node, pos = j + I = 3 next[pos] needs to insert before next[pos + 1]

When I = 2, source[5]! == -1 but I === seq[k] so I don’t do it, k–

When I = 1, source[4]! == -1 because I! == seq[k] so to move the node, pos = j + I = 1 next[pos] needs to insert before next[pos + 1]

When I = 0, source[5]! == -1 but I === seq[k] so I don’t do it, k–

The test code


const check = (diff) = > {
    var count = 100
    var changeCount = 100
    for(let i = 0; i < count; i++){
        var random = 0 + Math.floor(10 * Math.random())
        var prev = new Array(random).fill(0).map((item, index) = > {
            return {
                key: 'key-' + index,
                val: index
            }
        })
        for(let j = 0; j < changeCount; j++){
            var ran = random + (Math.floor(5 * Math.random()) * ( Math.random() > 0.5 ? 1 : -1))
            ran = ran > 0 ? ran : 0
            var next = new Array(ran).fill(0).map((item, index) = > {
                return {
                    key: 'key-' + index,
                    val: index * random
                }
            })
            shuff(next)
            var res = diff(prev, next)
            if(! eq(res, next)){console.log(prev, next)
            }
            prev = res
        }
    }
};

function shuff(arr){
    let len = arr.length - 1
    for(let i = 0; i < len; i++){
        let rand = i + Math.floor((len - i) * Math.random())
        swap(arr, rand, i)
    }
}

function swap(arr, i, j){
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}

console.log('random test')
console.time('diff1')
check(diff1)
console.timeEnd('diff1')

console.time('diff2')
check(diff2)
console.timeEnd('diff2')

console.time('diff3')
check(diff3)
console.timeEnd('diff3')

let size = 5000
let next = new Array(size).fill(0).map((item, index) = > {
    return {
        key: 'key-' + index,
        val: index
    }
})
let prev = JSON.stringify(next)
shuff(next)
next = JSON.stringify(next)

console.log('test')
console.time('diff1')
eq(diff1(JSON.parse(prev), JSON.parse(next)), next)
console.timeEnd('diff1')

console.time('diff2')
eq(diff2(JSON.parse(prev), JSON.parse(next)), next)
console.timeEnd('diff2')

console.time('diff3')
eq(diff3(JSON.parse(prev), JSON.parse(next)), next)
console.timeEnd('diff3')
Copy the code

The execution result