首页 > 解决方案 > 动画 DFS - 在 Javascript 对象中使用 setInterval 和 this 的问题

问题描述

我在 HTML 中有一个按钮网格,我想通过在第一次访问按钮时更改按钮的颜色,然后在完成访问时更改按钮的颜色,来为深度优先搜索设置动画。我正在使用 Javascript 对象来处理所有逻辑,并使用递归 DFS 方法来构建像[[animation_function, grid_coordinates], ... ]. 然后我使用该setInterval函数遍历整个队列。

DFSGrid问题是对象在运行时似乎消失了DFSGrid.animate()?如果我console.log(this)在 内DFSGrid.animate(),它似乎this是指 Window 对象?但this在所有其他方法中都可以正常工作。

我是网络东西的新手(真的是 Javascript),所以我非常困惑。我尝试了各种不同的东西,但我总是遇到一些问题,this当动画应该运行时,或者那个 (lol) 是未定义的。

我究竟做错了什么?

class DFSGrid {
    constructor(dfsGridElement) {
        this.dfsGridElement = dfsGridElement
        this.dirs = [[0, 1], [-1, 0], [0, -1], [1, 0]]
        this.visited = new Set()
        this.visited.add('00')
        this.queue = []
        this.idx = 0
    }

    dfs(i, j) {
        if (i > 5 || j > 3) {
            return
        }
        const coords = i.toString() + j.toString()
        this.queue.push([this.visit, coords])

        for (let d of this.dirs) {
            let [ni, nj] = [i + d[0], j + d[1]]
            let nextCoords = ni.toString() + nj.toString()
            if (0 <= ni && ni <= 5 && 0 <= nj && nj <= 3 && (!this.visited.has(nextCoords))) {
                this.visited.add(nextCoords)
                this.dfs(ni, nj)
            }
        }
        this.queue.push([this.finish, coords])
    }

    visit(coords) {
        // The grid of buttons has class selectors like:
        // <button class="c00">00</button>
        let cell = this.dfsGridElement.querySelector('.c' + coords)
        cell.style.backgroundColor = 'red'
    }

    finish(coords) {
        let cell = this.dfsGridElement.querySelector('.c' + coords)
        cell.style.backgroundColor = 'black'
    }

    animate() {
        console.log(this)
        let idx = this.idx
        if (idx > this.queue.length) {
            return
        }
        let [func, coords] = [this.queue[idx][0], this.queue[idx][1]]
        func(coords)
        this.idx += 1
    }
}

const dfsGridElement = document.querySelector('.dfs-grid')
const dfsGrid = new DFSGrid(dfsGridElement)
dfsGrid.dfs(0,0)
setInterval(dfsGrid.animate, 2000)
* {
  box-sizing: border-box;
  font-name: Gothic Rounded, sans serif;
}

.app-content {
  margin: 0px;
  padding: 0px;
  background: linear-gradient(to right, #00AAFF, #00FFA6);
  color: gray;
  height: 100%;
}

.dfs-grid {
  display: grid;
  min-height: 100vh;
  justify-content: center;
  align-content: center;
  grid-template-columns: repeat(4, 100px);
  grid-template-rows: minmax(120px, auto) repeat(5, 100px);
}
.dfs-grid button {
  cursor: pointer;
  font-size: 2rem;
  outline: none;
  border: 1px solid white;
  background-color: rgba(255, 255, 255, 0.75);
}
.dfs-grid button:hover {
  background-color: rgba(255, 255, 255, 0.9);
}
<div class="app-content">
    <div class="dfs-grid">
        <button class="cell c00">00</button>
        <button class="cell c01">01</button>
        <button class="cell c02">02</button>
        <button class="cell c03">03</button>
        <button class="cell c10">10</button>
        <button class="cell c11">11</button>
        <button class="cell c12">12</button>
        <button class="cell c13">13</button>
        <button class="cell c20">20</button>
        <button class="cell c21">21</button>
        <button class="cell c22">22</button>
        <button class="cell c23">23</button>
        <button class="cell c30">30</button>
        <button class="cell c31">31</button>
        <button class="cell c32">32</button>
        <button class="cell c33">33</button>
        <button class="cell c40">40</button>
        <button class="cell c41">41</button>
        <button class="cell c42">42</button>
        <button class="cell c43">43</button>
        <button class="cell c50">50</button>
        <button class="cell c51">51</button>
        <button class="cell c52">52</button>
        <button class="cell c53">53</button>
    </div>
</div>

标签: javascript

解决方案


原来我上面遇到的this问题在这里被描述为“问题”:https ://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

使用 时setIntervalthis被调用函数的关键字将是window对象。解决这个问题的一种方法是使用Function.prototype.bind

const startDFSAnimation = dfsGrid.animate.bind(dfsGrid)
startDFSAnimation()

上面将运行该dfsGrid.animate函数的新版本,但我们确保this它将引用dfsGrid它运行的时间。

编辑:正如@Caleb Miller 指出的那样,这也可以通过在我认为更好的类的构造函数中绑定来解决。

class DFSGrid {
    constructor(dfsGridElement) {
        // Do this for all class methods that are used by
        // setTimeout or setInterval functions
        this.animate = this.animate.bind(this)
        this.visit = this.visit.bind(this)
        this.finish = this.finish.bind(this)
    }


推荐阅读