首页 > 解决方案 > Javascript - 为什么我的函数是递归的?

问题描述

我不知道我哪里错了。为什么我的函数是递归的?我期望 sum = 3;

function init() {
  let object = {
    firsNum: 1,
    sum: 0,
  };
  add(1, object.firsNum, fun);
  console.log(object.sum);
}

function fun(a) {
  a.firstNum++;
  a.sum += a.firsNum;
  fun(a);
}

function add(a, b, callback) {
  return callback(a + b);
}
init();

标签: javascriptrecursioncallback

解决方案


我认为您并不完全理解您在代码中使用的每个编程概念。

除此之外,您的原始代码包含一个错字:a.firstNum++;它应该firsNum像所有其他地方一样阅读。

让我们将您的代码分解为单独的关注点。

递归

递归意味着函数调用自身。递归是一种计算、迭代或遍历数据的方法。while另一种是使用类似or的循环进行迭代for

让我们看一下这个简化的例子:

function recurse(counter) {
   console.log(counter);
   recurse(counter + 1);
}
recurse(0);

这将recurse()无限期地调用,因为没有什么可以阻止它调用自己。递归和迭代需要一些停止条件来中断递归。

如果您想在某个值处停止,则必须仅在满足该条件之前调用该函数:

function recurse(counter) {
   console.log(counter);
   if (counter < 42) {
      recurse(counter + 1);
   }
}
recurse(0);

这只会递归和递增,直到达到 42。

您甚至可以使用函数本身传递停止条件:

function recurse(counter, maxValue) {
   console.log(counter);
   if (counter < maxValue) {
      recurse(counter + 1, maxValue); // maxValue gets passed along
   }
}
recurse(0, 42);

要使您的递归函数停止在某个值,您必须添加这样一个条件:

function fun(a) {
  a.firstNum++;
  a.sum += a.firstNum;
  if (a.sum < a.maxNum) {
    fun(a);
  }
}

然后,您必须确保您的对象指定条件:

let object = {
  maxNum: 3, // stop condition added
  firsNum: 1,
  sum: 0,
};

虽然这本身并不能解决问题。继续阅读。

回调

回调是作为函数参数、对象属性或数组元素传递给其他函数的函数。

回调允许您决定在运行时调用什么函数,而不必使用iforswitch语句实现分支行为,并且在编写时需要某些函数名称。

function someCallback() { /* ... */ }

function callTheCallback(callback) {
   callback(); // Execute the parameter as a function
}

callTheCallback(someCallback);

let someCallbackReference = someCallback;

// Call the same function but indirectly via a variable
callTheCallback(someCallbackReference);

在上面的例子中someCallback,可以间接地源自另一个对象。

您甚至可以直接将函数表达式指定为回调:

callTheCallback(function() {
   // ...
});

对于回调,掌握传递函数调用结果和函数本身之间的区别至关重要:

callTheCallback(someCallback); // Callback function passed
callTheCallback(someCallback()); // Result of a function call passed

请注意,如果返回值本身是一个函数,则传递函数调用的结果可能是完全有效的。这在编程中也很常见。

回调通常用于控制反转 (IoC),特别是异步编程

回调函数示例:

在您的情况下fun(),作为第三个参数回调传递给add().

既然您知道如何使用递归和回调,您的代码中仍然存在语义错误......

语义错误

与语法错误(错误的代码语法)相反,语义错误是与代码含义有关的错误。

接口add()

分析您的函数API

function add(a, b, callback) {
  return /* some value - callback() call removed for simplicity */;
}

add()接受三个参数返回一个值。第三个参数被命名为“回调”,因此应该是一个函数。

add()尽管您不使用返回值(这不一定是错误),但您的调用看起来很明智。

add(1, object.firsNum, fun);

实施add()

由于add()没有记录的参数,因此您的意图是模棱两可的。

名称以及存在两个参数a和的事实b导致人们假设应该添加两个值。你很好地做到了这一点:

return callback(a + b);

但!您作为回调函数传递的内容 - 即fun()- 需要不同的参数,而不是数字。fun()期望一个对象。

正确记录它看起来像这样:

/**
 * Recursively sum values until a maximum value is reached.
 * 
 * @param {Object} a object containing the sum, the increment and the max value
 * @param {Number} a.firsNum start value
 * @param {Number} a.maxNum the maxium value up to which to add "firsNum" to "sum"
 * @param {Number} a.sum the summed value, shall be 0 initially
 * @returns {undefined} nothing is returned
 */
function fun(a) {
  // ...
}

上面的注释样式叫做JsDoc:https ://jsdoc.app

注意为函数、变量和参数使用正确的名称。正确的名称有助于在不编写明确的文档注释的情况下记录代码。

修复错误

由于对您实际想要达到的目标的了解有限,因此只能推测。我认为add(),虽然它的名称和参数看起来很合理,但实施错误。您的问题可以通过fun()正确调用回调 ( ) 来解决:

function add(obj, callback) {
  return callback(obj);
}

和:

add(object, fun);

由于我们需要该对象add(),因此现在期望它作为单个参数。

好吧,现在这已经没有多大意义了。fun()可以直接从内部调用,init()而不是通过回调引用,并且add()名称令人困惑。

我不会进一步推测您的意图是什么以及如何实施该问题的替代解决方案。

你学到了什么

  • 关于Mozilla 开发者网络
  • 递归的工作原理
  • 函数可以作为参数传递并分配给变量或属性
  • 读取代码
  • 记录代码和正确命名函数和变量的好处

推荐阅读