Javascript 与es6类和jquery的绑定

Javascript 与es6类和jquery的绑定,javascript,jquery,ecmascript-6,Javascript,Jquery,Ecmascript 6,在处理一个项目时,我注意到,这个类内方法在通过jquery调用访问时并没有真正附加到实例,甚至也没有附加到类。相反,我看到的是窗口全局,这是不对的 因此,有多种方法可以为您自动绑定。。我试过几次,但都没成功。发生了什么事 以下是错误: Uncaught TypeError: this.chooseAtRandom is not a function at move (pen.js:83) move @ pen.js:83 pen.js:102 Uncaught TypeError: Ca

在处理一个项目时,我注意到,
这个
类内方法在通过jquery调用访问时并没有真正附加到实例,甚至也没有附加到类。相反,我看到的是
窗口
全局,这是不对的

因此,有多种方法可以为您自动绑定。。我试过几次,但都没成功。发生了什么事

以下是错误:

Uncaught TypeError: this.chooseAtRandom is not a function
    at move (pen.js:83)
move @ pen.js:83
pen.js:102 Uncaught TypeError: Cannot read property 'push' of undefined
    at UIController.animate (pen.js:102)
    at HTMLDivElement.<anonymous> (pen.js:131)
    at HTMLDivElement.dispatch (jquery.min.js:3)
    at HTMLDivElement.q.handle (jquery.min.js:3)
animate @ pen.js:102
(anonymous) @ pen.js:131
dispatch @ jquery.min.js:3
q.handle @ jquery.min.js:3
pen.js:83 Uncaught TypeError: this.chooseAtRandom is not a function
    at move (pen.js:83)
注意这里的
Binder.getAllMethods
调用。如果我把它改成this.getAllMethods,我也会遇到一个问题。我想知道codepen是不是把它弄坏了

下面是我在使用它:

class SimonAudio {
  constructor() {
    this.soundsrcs = [
      'https://s3.amazonaws.com/freecodecamp/simonSound1.mp3',
      'https://s3.amazonaws.com/freecodecamp/simonSound2.mp3',
      'https://s3.amazonaws.com/freecodecamp/simonSound3.mp3',
      'https://s3.amazonaws.com/freecodecamp/simonSound4.mp3'
    ]
    this.players = this.soundsrcs.map(s => {
      let a = document.createElement('audio');
      a.setAttribute('src', s)
      return a
    })
    this.uiMap = {
      red: 0,
      green: 1,
      amarillo: 2,
      blue: 3
    }

    Binder.bind(this, SimonAudio)
  }
  play(uiId) {
    this.players[this.uiMap[uiId]].play()
  }
}

class Game {
  constructor(UI) {
    this.UI = UI
    this.uiMap = {
      red: 0,
      green: 1,
      amarillo: 2,
      blue: 3
    };
    this.dexUi = ['red', 'green', 'amarillo', 'blue']
    this.states = ['SHOWINGCHALLENGE', 'LISTENING']
    this.audio = new SimonAudio()
    this.moves = []
    this.gameStack = []
    this.inGame = false

    Binder.bind(this, Game)
  }

  start() {
    this.inGame = true
    this.gameStack.push(setTimeout(this.move, parseInt((Math.random() + 1) * 1000)))
  }
  restart() {
    this.moves = []
    this.gameStack.forEach(a => {
      clearTimeout(a)
    })
    this.gameStack = []
    this.inGame = false
  }
  playMoves() {
    let elf = this.UI
    this.moves.forEach(m =>
      this.gameStack.push(
        setTimeout(function() {
          elf.animate(m)
        }, parseInt((Math.random() + 1) * 500)))
    )
  }
  move() {
    let move = this.chooseAtRandom()
    this.moves.push(move)
    this.playMoves()
  }
  chooseAtRandom() {
    return this.dexUi[parseInt(Math.random() * 4)]
  }
}

class UIController {
  contructor() {
    this.animation_stack = []
    Binder.bind(this, UIController)
  }
  clear() {
    this.animation_stack.forEach(a => clearTimeout(a))
    this.animation_stack = []
  }
  animate(uiId) {
    $(`#${uiId}`).addClass('highlight')
    this.animation_stack.push(setTimeout(function() {
      $(`#${uiId}`).removeClass('highlight')
    }, 333))
  }
}
然后,jquery将其放大:

$(function() {
  let UI = new UIController()
  let Simon = new Game(UI)

  $('#strict').click(function() {
    $(this).toggleClass('toggled')
  })

  $('#restart').click(function() {
    $('.controls .button').removeClass('disabled toggled')
    $('.game-buttons div').addClass('disabled')
    Simon.restart()
  })

  $('#start').click(function() {
    if (Game.inGame)
      return
    $('.game-buttons > div').addClass('hvr-radial-out')
    $(this).addClass('toggled')
    $('#strict').addClass('disabled')
    $('.game-buttons div').removeClass('disabled')
    Simon.start()
  })

  $('.game-buttons div').addClass('disabled')
    .click(function() {
      if ($(this).hasClass('disabled'))
        return
      let id = $(this).attr('id')
      UI.animate(id)
      Simon.audio.play(id)
    })

})

据我所知,绑定通常用于将一个函数或多个函数绑定到一个对象

例如,使用JS的绑定:

this.start.bind(this);
或使用lodash的bind/bindAll:

_.bind(this.start, this);
_.bindAll(this, ['start', 'restart']);
在这些情况下,“this”指的是游戏类的一个实例。我相信这将确保绑定方法总是在游戏实例的上下文中被调用

您的方法似乎不太符合这种用法

我建议使用lodash bind/bindAll实现,而不是尝试自己实现

资源

还有,太棒了,你在做一个西蒙游戏

未捕获类型错误:this.chooseAtRandom不是函数

好的,让我们看看调用代码

move() {
  let move = this.chooseAtRandom()
  this.moves.push(move)
  this.playMoves()
}
好的,就在这里,当函数实际启动时,您失去了希望保留的
上下文。我们可以通过几种方式轻松地重写它。但在我开始复制/粘贴代码中另一个非常糟糕的部分之前,我必须先修复它

// parseInt is for converting strings to numbers
// Math.random() gives you a number so you don't need to parse it
// change this
parseInt((Math.random() + 1) * 1000)

// to this
(Math.random() + 1) * 1000
好的,现在让我们修复函数上下文

// Option 1: use Function.prototype.bind
this.gameStack.push(setTimeout(this.move.bind(this), (Math.random() + 1) * 1000))

// Option 2: use a sexy ES6 arrow function
this.gameStack.push(setTimeout(() => this.move(), (Math.random() + 1) * 1000))
好的,现在让我们检查是否使用正确的上下文调用了
start

$('#start').click(function() {
  if (Game.inGame)
    return
  $('.game-buttons > div').addClass('hvr-radial-out')
  $(this).addClass('toggled')
  $('#strict').addClass('disabled')
  $('.game-buttons div').removeClass('disabled')
  Simon.start()
})
$(“#开始”)。单击(函数(){
如果(Game.inGame)
返回
$('.game buttons>div').addClass('hvr-radial-out'))
$(this.addClass('toggled'))
$('#strict').addClass('disabled'))
$('.game buttons div')。removeClass('已禁用')
Simon.start()
})
好的!
start
函数中的
this
将设置为
Game
Simon
实例。我无法运行您的其余代码,因此我不知道是否还有其他问题,但这应该可以解决
此问题。chooseAtRandom
不是您以前看到的函数错误


好。

它似乎非常接近于
boundGetX
示例。。它有什么显著的不同?
var boundGetX=retrieveX.bind(模块)--在boundGetX示例中,对象被绑定到函数(retrieveX)
Binder.bind(这个,游戏)
——在您的示例中,您试图将对象绑定到类(Binder)。很抱歉,我没有正确查看您的Binder代码。我想我可能知道你现在想做什么了。不,谢谢,但是你没有正确阅读代码。我正在绑定类方法<代码>绑定。绑定是一个自定义方法,它为对象的每个方法或属性处理绑定,对于给定的任何类,我都是从中获得的,它可能不正确,但看起来差不多正确。。我不知道你是否需要一个自定义绑定实现,但我想我用lodash的bind和bindAll方法替换了你的自定义代码,从而使游戏正常运行。“开始”和“重新启动”按钮似乎仍在工作:。在我看来,对于像bind这样的东西,使用本机或第三方方法通常比尝试自己的方法要好,这并不是说尝试有什么错。这很好,真的很好,谢谢。我刚刚发现这是在和另一个到目前为止回答的人谈话。但是从您寻址的对
moves()
的调用向下两行是对
playMoves
的调用,该调用引用内部
UIController
。这也触发了setTimeout调用,但以类似的方式更改它们并不能解决问题:`this.gameStack.push(setTimeout(function(){elf.animate(m).bind(elf)},parseInt((Math.random()+1)*500)))`你有没有遵循一些指导来做这件事,或者你是自己来做这些课程的?自动装订非常混乱,可能是我看不出的其他问题的原因。我没有遵循指南,不。我不明白你为什么认为这很混乱。我确实是从别人那里得到的。。在任何情况下,我都不喜欢它,特别是因为它没有;似乎无法像这样解析嵌套类。总之,另一个人找到了一个有效的解决办法。配合lodash@robertotom首先,您实现的
Binder
与该要点不同。gist为
getAllMethods
bind
提供了静态类方法,而您的gist是通过实例方法实现的。第二,我觉得自动绑定适用于那些不太了解动态函数上下文的人;ie想象起来很可怕,为了编写一个简单的游戏,必须将每个类中的每个方法绑定到一个已知的上下文。不管它值多少钱,我已经有将近两年没有在生产代码中使用
Function.prototype.bind
。只是没那么重要。。。。如果你如此依赖它,我认为人们对JavaScript的工作原理有一个根本性的误解。第三,这是我见过的最伤脑筋的命题逻辑表达:
!(!(函数的方法实例)| |方法===cls)–可以用更直接的方式表示为函数和方法的
methodinstanceof Function&&method!==cls–另请参见:
// Option 1: use Function.prototype.bind
this.gameStack.push(setTimeout(this.move.bind(this), (Math.random() + 1) * 1000))

// Option 2: use a sexy ES6 arrow function
this.gameStack.push(setTimeout(() => this.move(), (Math.random() + 1) * 1000))
$('#start').click(function() {
  if (Game.inGame)
    return
  $('.game-buttons > div').addClass('hvr-radial-out')
  $(this).addClass('toggled')
  $('#strict').addClass('disabled')
  $('.game-buttons div').removeClass('disabled')
  Simon.start()
})