与es6类和jquery绑定

时间:2016-12-21 01:31:00

标签: javascript jquery ecmascript-6

在一个项目上工作我注意到,当通过jquery调用访问时,类方法中的this并没有真正附加到实例,甚至类都没有。相反,我看到的是window全局,而且这是不对的。

所以有各种各样的方法可以为你自动提出..我尝试了一对,而且 - 没有成功。发生了什么事?

这是错误:

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)

这是我的autobind方法:

class Binder {
  getAllMethods(instance, cls) {
  return Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
    .filter(name => {
      let method = instance[name]
      return !(!(method instanceof Function) || method === cls)
    })
  }
  bind(instance, cls) {
  Binder.getAllMethods(instance, cls)
    .forEach(mtd => {
      instance[mtd] = instance[mtd].bind(instance);
    })
  }
}

请注意那里的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)
    })

})

2 个答案:

答案 0 :(得分:2)

  

未捕获的TypeError:this.chooseAtRandom不是函数

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

move() {
  let move = this.chooseAtRandom()
  this.moves.push(move)
  this.playMoves()
}

好的,我们来看看move现在的调用代码

start() {
  this.inGame = true
  this.gameStack.push(setTimeout(this.move, parseInt((Math.random() + 1) * 1000)))
}

好的,在那里你失去了你希望在函数实际触发时保留的this上下文。我们可以通过几种方式轻松地重写它。但在我开始复制/粘贴代码中另一个非常糟糕的部分之前,我必须首先修复它。

// 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()
})
好的! this函数中的start将设置为Simon Game个实例。我无法运行其余代码,所以我不知道是否还有其他问题,但是应该修复this.chooseAtRandom不是您之前看到的函数错误。

行。

答案 1 :(得分:1)

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

例如使用JS&#39; bind:

this.start.bind(this);

或者使用lodash的bind / bindAll:

_.bind(this.start, this);
_.bindAll(this, ['start', 'restart']);

&#34;这&#34;在这些情况下,将引用Game类的实例。我相信这将确保在游戏实例的上下文中始终调用绑定方法。

您的方法似乎没有遵循这种用法。

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

<强>资源

P.S。真是太棒了,你正在制作西蒙游戏!