为何setInterval无法调用自定义对象方法?附动画代码问题
问题分析与修复方案
咱们一步步拆解你代码里的问题,解决这个「fn is not a function」报错和动画静止的问题:
1. 直接触发报错的核心问题:传了函数执行结果而非函数本身
你调用 runOnInterval(tesla.moveRight(), 10) 时加了括号 (),这会立即执行moveRight方法,并把它的返回值(这里moveRight没有return,所以是undefined)传给runOnInterval的fn参数。当setInterval里尝试调用fn(arg)时,fn是undefined,自然会抛出「fn is not a function」的错误。
修复方式:去掉括号,传递函数引用即可:
runOnInterval(tesla.moveRight, 10);
2. moveRight与draw方法的逻辑漏洞
- 首先,
moveRight里的this.x;是完全无效的代码,你需要更新实例的x坐标,应该改成this.x += distance;,这样每次调用才会累加位置,实现持续移动的效果。 - 其次,你直接把
distance赋值给left,而不是更新后的this.x,这样每次都会把车定位到10px的位置,自然看不到移动。另外,draw方法里只创建了carHtml字符串,但没有把元素挂载到DOM,也没赋值给this.carElement,导致moveRight里调用this.carElement.css()时会报错。
修复draw和moveRight方法:
Car.prototype.draw = function() { var carHtml = '<img src="http://nostarch.com/images/car.png">'; // 创建DOM元素并添加到页面 this.carElement = $(carHtml); $('body').append(this.carElement); // 初始化车辆位置 this.carElement.css({ left: this.x, top: this.y }); }; Car.prototype.moveRight = function(distance) { // 更新x轴坐标 this.x += distance; // 应用新的位置 this.carElement.css({ left: this.x, top: this.y }); };
3. 容易忽略的this指向丢失问题
即使你传了tesla.moveRight给runOnInterval,在setInterval的回调里调用fn(arg)时,moveRight里的this会指向window(而非tesla实例),导致this.x和this.carElement无法正确访问。
解决方式:两种常用方案任选其一:
- 方案一:传参时用
bind绑定实例:
runOnInterval(tesla.moveRight.bind(tesla), 10);
- 方案二:修改
runOnInterval,支持传入上下文:
function runOnInterval(fn, arg, context) { setInterval(function () { fn.call(context, arg); }, 1000); } // 调用时传入tesla作为上下文 runOnInterval(tesla.moveRight, 10, tesla);
完整修复后的代码示例
var Car = function(x, y) { this.x = x; this.y = y; }; Car.prototype.draw = function() { var carHtml = '<img src="http://nostarch.com/images/car.png">'; this.carElement = $(carHtml); $('body').append(this.carElement); this.carElement.css({ left: this.x, top: this.y }); }; Car.prototype.moveRight = function(distance) { this.x += distance; this.carElement.css({ left: this.x, top: this.y }); }; var tesla = new Car(20, 20); tesla.draw(); function runOnInterval(fn, arg, context) { setInterval(function () { fn.call(context, arg); }, 1000); } runOnInterval(tesla.moveRight, 10, tesla);
内容的提问来源于stack exchange,提问作者Michael Alexander
相关产品推荐
相关产品推荐

