javascript 如何传递原型函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11387127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 13:00:55  来源:igfitidea点击:

How to pass prototype function?

javascriptprototype

提问by user1510539

I'm a newbie to javascript and I need some help. I was trying to sum radius by function, but got an undefined error:(

我是 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);

回答by zetlen

The problem is that you're passing a function a reference to another function, and the passed function is therefore losing scope! Here's the offending line:

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

Circle.prototype.increaseRadiusBy = function(number) {
    this.r = sumWithFunction(this.getRadius, number);
}

JavaScript objects are in some ways simpler than they appear. When you added the getRadiusmethod to the Circleprototype, you were not defining a class method like you would in classical OO. You were simply defining a named property of the prototype, and assigning a function to the value of that property. When you pass this.getRadiusas an argument to a static function, like sumWithFunction, the context of thisis lost. It executes with the thiskeyword bound to window, and since windowhas no rproperty, the browser throws an undefined error.

JavaScript 对象在某些方面比它们看起来更简单。当您将getRadius方法添加到Circle原型时,您并没有像在经典 OO 中那样定义类方法。您只是定义了原型的命名属性,并为该属性的值分配了一个函数。当您将this.getRadius参数作为参数传递给静态函数时,例如sumWithFunction, 的上下文将this丢失。它使用this绑定到的关键字执行window,并且由于window没有r属性,浏览器会抛出未定义的错误。

Put another way, the statement this.getRadius()is actually saying "execute the function assigned to the getRadiusproperty of this, and execute it in the contextof this.Without calling the function explicitly through that statement, the context is not assigned.

换句话说,该语句this.getRadius()实际上是在说“执行分配给 的getRadius属性的函数this,并在 的上下文中执行它this没有通过该语句显式调用该函数,上下文未分配。

A common solution to this is to add an expected argument to any function which receives another function, for context.

对此的常见解决方案是向任何接收另一个函数的函数添加一个预期参数,用于上下文。

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);

A simpler, but less robust solution would be to declare a function inline that can access a context reference in the local closure.

一个更简单但不太健壮的解决方案是声明一个内联函数,该函数可以访问本地闭包中的上下文引用。

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

But by far the simplest solution is to use a newer feature of ECMAScript, a function method called bind. It is explained well here, including the fact that it is not supported by all browsers. That's why a lot of libraries, like jQuery, Prototype, etc., have cross-browser function-binding utility methods like $.proxy.

但到目前为止,最简单的解决方案是使用 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)

回答by vkarpov15

The tricky thing with thisin JavaScript is that it contains the object the function was a property of when it was calledby default. So when you pass MyCircle.increaseRadiusByas a parameter, you then call it as func(), so the function isn't a property of any object. The easiest way to set thisis to use the call()function:

thisJavaScript 中的棘手之处在于它包含该函数是默认调用时的属性的对象。因此,当您MyCircle.increaseRadiusBy作为参数传递时,您将其称为 as 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);

The below approach, setting funcas a property before calling it, also works. You would never do this in practice because it adds an unnecessary property to context, but it's a good didactic example to show how thisworks.

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

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

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