在一个项目上工作我注意到,当通过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)
})
})
答案 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。真是太棒了,你正在制作西蒙游戏!