You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在另一实例对象的上下文中调用目标实例的方法?

解决JavaScript中回调函数上下文绑定的问题

嘿,这个问题简直是JS新手必踩的经典坑——回调函数的this指向不对!我来给你拆解清楚问题出在哪,以及几种简单的解决办法:

问题根源

你代码里的监听器函数在OnChangeManager.motionEvent()里被直接调用(listener()),这时候this的指向是当前的执行上下文,也就是OnChangeManager实例,而不是你期望的Ball实例。毕竟JS里的this是调用时绑定的,不是定义时绑定的~

另外先提两个代码里的小错误,不修复的话也会出问题:

  • OnChangeManager的constructor少了括号,应该是constructor(){}
  • Ball.addMotionListener里拼写错误:_onChnangeManager → _onChangeManager,addMotionListner → addMotionListener
  • 遍历数组别用for...in,它会遍历数组的原型属性,要用for...of或者普通for循环

解决方案(三种任选其一)

方案1:绑定this在添加监听器时

在game.js里给回调函数用bind()方法,把它的this固定绑定到ball实例上:

var ball = new Ball();
// 用bind把回调的this绑定到ball
ball.addMotionListener(function(){
  this._color = someNewRandomColor();
}.bind(ball));

bind()会返回一个新函数,这个函数的this永远是你传入的ball,不管在哪调用它。

方案2:用ES6箭头函数

箭头函数没有自己的this,它会继承外层作用域的this。在game.js里用箭头函数的话,外层作用域的this就是ball(因为是在调用ball.addMotionListener的上下文里):

var ball = new Ball();
ball.addMotionListener(() => {
  this._color = someNewRandomColor();
});

这个方法更简洁,适合ES6+的环境。

方案3:调用监听器时指定this

修改OnChangeManager的motionEvent方法,让它接收一个上下文参数,调用监听器时用call()指定this:

首先修改Ball类的position setter,把当前实例传进去:

set position(newP){
  this._position = newP;
  // 把Ball实例传给motionEvent
  this._onChangeManager.motionEvent(this);
}

然后修改OnChangeManager的motionEvent:

motionEvent(context){
  // 遍历监听器队列,用call指定this为传入的context(Ball实例)
  for(const listener of this._motionListenerQueue){
    listener.call(context);
  }
}

这种方法的好处是不用修改调用方的代码,把上下文绑定的逻辑放在事件管理器里。

修复后的完整代码示例

game.js

// 先实现一个生成随机颜色的函数
function someNewRandomColor() {
  const letters = '0123456789ABCDEF';
  let color = '#';
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

var ball = new Ball();
// 选方案1或方案2的话用下面的代码
ball.addMotionListener(() => {
  this._color = someNewRandomColor();
});

// 模拟kickBall修改position
function kickBall(ball) {
  ball.position = new Vector(10, 20);
}
kickBall(ball);

Ball.js

Ball = class{
 constructor(){
  this._position = new Vector(0,0);
  this._color = 'red'; // 字符串要加引号,不然会被当成未定义变量
  this._onChangeManager = new OnChangeManager();
 }
 addMotionListener(listener){
  this._onChangeManager.addMotionListener(listener); // 修复拼写错误
 }
 set position(newP){
  this._position = newP;
  // 方案1、2用这个:
  this._onChangeManager.motionEvent();
  // 方案3用这个:
  // this._onChangeManager.motionEvent(this);
 }
}

OnChangeManager.js

OnChangeManager = class{
 constructor(){ // 修复括号缺失
  this._motionListenerQueue = [];
 }
 addMotionListener(newListener){
  this._motionListenerQueue.push(newListener);
 }
 // 方案1、2对应的motionEvent
 motionEvent(){
  for(const listener of this._motionListenerQueue){
   listener();
  }
 }
 // 方案3对应的motionEvent(如果用方案3就注释掉上面的,用这个)
// motionEvent(context){
//  for(const listener of this._motionListenerQueue){
//   listener.call(context);
//  }
// }
}

这样修改后,监听器里的this就会指向ball实例,就能正常修改它的_color属性啦!

内容的提问来源于stack exchange,提问作者Jonzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:43:02