9

我是 javascript 的新手,我需要一些帮助。我试图按函数对半径求和,但得到一个未定义的错误:(

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, number);
}

function addFivetoIt(func) {
    func(5);
}

var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy);
4

2 回答 2

27

问题是您正在传递一个函数对另一个函数的引用,因此传递的函数失去了作用域!这是违规行:

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)
于 2012-07-08T22:26:27.170 回答
0

JavaScript 中的棘手this之处在于它包含该函数在默认情况下被调用时的属性的对象。MyCircle.increaseRadiusBy因此,当您作为参数传递时,然后将其调用为func(),因此该函数不是任何对象的属性。最简单的设置方法this是使用call()函数:

function addFivetoIt(func, context) {
    // The first parameter to `call()` is the value of `this` in the function
    func.call(context, 5);
}

var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, MyCircle);

下面的方法,func在调用它之前设置为属性,也有效。在实践中你永远不会这样做,因为它为 增加了一个不必要的属性context,但它是展示如何this工作的一个很好的教学示例。

function addFivetoIt(func, context) {
    context.func = func;
    context.func(5);
}

var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, MyCircle);
于 2020-01-03T19:03:01.353 回答