问题是您正在传递一个函数对另一个函数的引用,因此传递的函数失去了作用域!这是违规行:
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, number);
}
JavaScript 对象在某些方面比它们看起来更简单。当您将getRadius
方法添加到Circle
原型时,您并没有像在经典 OO 中那样定义类方法。您只是定义原型的命名属性,并将函数分配给该属性的值。当您this.getRadius
作为参数传递给静态函数时,例如sumWithFunction
, 的上下文this
会丢失。它使用this
绑定到的关键字执行window
,并且由于window
没有r
属性,浏览器会抛出未定义的错误。
换句话说,该语句this.getRadius()
实际上是在说“执行分配给 的getRadius
属性的函数,并在 的上下文中this
执行它。this
如果不通过该语句显式调用函数,则不会分配上下文。
一个常见的解决方案是为任何接收另一个函数的函数添加一个预期的参数,作为上下文。
function sumWithFunction(func, context, number) {
return func.apply(context) + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, this, number);
}
function addFivetoIt(func, context) {
func.apply(context,[5]);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, myCircle);
一个更简单但不太健壮的解决方案是声明一个内联函数,该函数可以访问本地闭包中的上下文引用。
function sumWithFunction(func, number) {
return func() + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
var me = this;
this.r = sumWithFunction(function() {
return me.getRadius()
}, number);
}
function addFivetoIt(func) {
func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(function(number) {
return MyCircle.increaseRadiusBy(number);
});
但到目前为止,最简单的解决方案是使用 ECMAScript 的一个新特性,一个名为bind
. 这里解释得很好,包括并非所有浏览器都支持它的事实。这就是为什么很多库,比如 jQuery、Prototype 等,都有跨浏览器的函数绑定实用方法,比如$.proxy
.
function sumWithFunction(func, number) {
return func() + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius.bind(this), number); // or $.proxy(this.getRadius,this)
}
function addFivetoIt(func) {
func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy.bind(MyCircle)); // or $.proxy(MyCircle.increaseRadiusBy,MyCircle)