【问题标题】:Function returning a function doesn’t throw an error despite the argument being undefined尽管参数未定义,但返回函数的函数不会引发错误
【发布时间】:2021-04-21 17:51:02
【问题描述】:

我在一个教程中看到了这段代码 sn-p:

const myFunction = () => {
   return function (caller) {
     caller(firstFuctnion());
     caller(secondFunction());
   };
};

你能告诉我一旦我这样称呼它是如何工作的:

myFunction()

caller 参数实际上没有定义,我怎么没有报错?

另一方面,如果我省略它并编写如下代码:

const myFunction = () => {
   return function () {
     firstFuctnion();
     secondFunction();
   };
 };

firstFuction()secondFunction() 这两个函数没有被执行。那么,caller — 或者不管它叫什么 — 究竟是如何工作的?

对于那些可能想查看整个功能代码的人:

const redux = require("redux");
const thunkMiddleware = require("redux-thunk").default;
const axios = require("axios");
const createStore = redux.createStore;
const applyMiddleware = redux.applyMiddleware;

const initialState = {
  loading: false,
  users: [],
  error: "",
};

const FETCH_USERS_REQUEST = "FETCH_USERS_REQUEST";
const FETCH_USERS_SUCCESS = "FETCH_USERS_SUCCESS";
const FETCH_USERS_FAILURE = "FETCH_USERS_FAILURE";

const fetchUsersRequest = () => {
  return {
    type: FETCH_USERS_REQUEST,
  };
};

const fetchUsersSuccess = (users) => {
  return {
    type: FETCH_USERS_SUCCESS,
    payload: users,
  };
};

const fetchUsersFailure = (error) => {
  return {
    type: FETCH_USERS_FAILURE,
    payload: error,
  };
};

const fetchUsers = () => {
  return function (dispatch) {
    dispatch(fetchUsersRequest());
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((response) => {
        // response.data is the users
        const users = response.data.map((user) => user.id);
        dispatch(fetchUsersSuccess(users));
      })
      .catch((error) => {
        // error.message is the error message
        dispatch(fetchUsersFailure(error.message));
      });
  };
};

const reducer = (state = initialState, action) => {
  console.log(action.type);
  switch (action.type) {
    case FETCH_USERS_REQUEST:
      return {
        ...state,
        loading: true,
      };
    case FETCH_USERS_SUCCESS:
      return {
        loading: false,
        users: action.payload,
        error: "",
      };
    case FETCH_USERS_FAILURE:
      return {
        loading: false,
        users: [],
        error: action.payload,
      };
  }
};

const store = createStore(reducer, applyMiddleware(thunkMiddleware));
store.subscribe(() => {
  console.log(store.getState());
});
store.dispatch(fetchUsers());

我不明白这部分。 dispatch 实际上做了什么。它没有作为参数传递,也没有在任何地方定义它是什么。是功能吗?我可以在那里写whatever

const fetchUsers = () => {
   return function (dispatch) {
     dispatch(fetchUsersRequest());
     axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((response) => {
      // response.data is the users
      const users = response.data.map((user) => user.id);
      dispatch(fetchUsersSuccess(users));
  })
  .catch((error) => {
    // error.message is the error message
    dispatch(fetchUsersFailure(error.message));
  });

}; };

【问题讨论】:

  • 相关:Functions that return a function。 “caller 参数实际上没有定义”是什么意思?可以这么说,那个论点还没有进入等式。 myFunction() 是一个函数,您需要为其提供 caller 参数,例如myFunction()(alert)。或者换一种说法,“我怎么没有收到错误” — 与function sum(a, b){ return a + b; }; sum; 没有抛出错误的原因相同,尽管没有提供ab;需要这些参数的函数还没有被调用。
  • 需要使用myFunction()()才能到达内部函数
  • 你已经得到了一些很好的答案,所以我不打算写论文了。在 redux-thunk 的情况下,thunk 中间件允许您 dispatch 一个动作,它是 dispatch 的函数,而不仅仅是一个普通的动作对象。内部函数在函数被分派到 store 时由 thunk 中间件调用。
  • 谢谢@LindaPaiste,这也是我缺少的部分。由于 redux-thunk,dispatch 接受函数。
  • 你没有调用你的函数,仅此而已。

标签: javascript redux higher-order-functions


【解决方案1】:

函数被视为任何其他值,这意味着它们可以从函数返回。 调用myFunction 将简单地返回内部函数,它需要在调用自身时将参数caller 传递给它。

内部函数会记住任何传递给它的封闭函数的参数。

我用来理解这个概念的一个例子是“乘数工厂”函数

const answer = function() {
    console.log("The answer is: ");
}

const multiplier = function(factor) {
    return function(n){
        answer();
        console.log( n * factor);
    };
}

const twice = multiplier(2);
const triple = multiplier(3);

twice(4); // The answer is: 8
triple(5); // The answer is: 15

在上面的代码中twicetriple 本质上如下

const twice = function(n) {
    console.log("The answer is: ");
    console.log(n * 2);
}

const triple = function(n) {
    console.log("The answer is: ");
    console.log(n * 3);
}

twice(4); // The answer is: 8
triple(5); // The answer is: 15

要了解更多关于这些概念的信息,我建议阅读 Javascript 中的“高阶函数”和“闭包”

【讨论】:

  • 感谢鲁班的解释。请再问一个问题:const twice = multiplier(2) 来自您的第一个 sn-p。那个号码是2 factor 还是n?或者两者都有:)我仍然没有得到你期望两个参数:factorn,但你最终只传递了一个数字2。谢谢你,祝你有美好的一天!
  • @VaclavVlcek multiplier 是一个接受factor 作为参数并返回一个函数的函数。所以,在multiplier(2) 中,factor 变成了2。此时你不需要考虑内部函数。在精神上,这只是const multiplier = function(factor){ return ……某事……; };。没有一个函数接受两个参数,所以你不要传递两个参数。
【解决方案2】:

让我们考虑一下这段代码的简化版本。 在您为您的问题添加一些重要的上下文之前(myFunction() 实际上正在被使用,以及它是如何使用的),我不太确定您的困惑在哪里,所以这个答案从回调的一般解释开始,更高-order 函数和柯里化。

首先,让我们把function 变成一个箭头函数,这样代码更容易阅读。 它们之间有differences,但我们暂时忽略它们。 让我们也将myFunction 重命名为outer,这样我们就知道我们在谈论哪个函数了。

const outer = () => {
   return (caller) => {
     caller(something);
   };
};

outer 是一个函数。 outer() 也是一个函数;也就是说,函数outer 的返回值本身就是一个函数。

由于outerfunction that returns a function,这使得outer 成为higher-order function (HOF)(根据HOF 的两个定义之一)1

明确地说,您可以使用另一个变量来引用返回的函数:

const inner = outer();

caller 参数实际上没有定义,我怎么没有报错?

这是一个奇怪的问题;当outer() 被调用时,它还没有触及caller。 只有调用 inner 才可以。

问问自己:为什么没有在此处定义 ab 时收到错误消息?2

const sum = (a, b) => a + b;

sum;

没错:sum 甚至没有被调用! 就像 outer() 甚至没有被调用一样。

即使更进一步,即使somethingUndefined 没有在任何地方定义并且不能在任何地方设置,这也不会引发错误。 (但您会收到 linter 警告。)

const someFunction = () => somethingUndefined;

someFunction;

尽管b 没有在add(5) 分配给addFive 的位置定义,但为什么在这个常见的HOF 示例中没有出现错误?

const add = (a) => {
  return (b) => a + b;
};
// Or more simply:
// const add = (a) => (b) => a + b;

const addFive = add(5);

console.log(addFive(3)); // Pints 8.

因为需要b的函数,即addFive,还没有被调用。

如果由于caller 未定义而要强制出错,则必须调用实际需要定义caller 的函数。有问题的函数是outer()!像任何其他函数一样,您可以使用 (): outer()() 来调用它。现在你得到一个TypeError,因为caller 不是一个函数。

这个“双重调用”被称为currying


代码也可以这样重写。 唯一的区别是inner 现在也可以在outer 所在的外部范围内访问。 见What is the scope of variables in JavaScript?

const inner = (caller) => {
  caller(something);
};
const outer = () => inner;

inner 扮演返回函数的角色 (outer())。 它接受一个函数作为参数,并以something 作为参数调用它。

outer 只是一个返回函数inner 的函数,仅此而已。 它不调用它,它不触及caller,它与something无关。

outer()inner 相同。


另一方面,如果我省略它并编写代码[没有caller],则firstFuction()secondFunction()这两个函数不会被执行。那么,caller — 或者不管它叫什么 — 究竟是如何工作的?

首先,让我们修复语义:firstFuction()secondFunction() 是函数调用。 它们是否函数,取决于firstFuctionsecondFunction 返回的内容。 firstFuctionsecondFunction 函数(至少代码暗示它们是)。

更具体地说,考虑alert

  • alert 是一个函数;你可以这样称呼它alert()
  • alert() 不是一个函数;你不能像alert()() 这样称呼它。

caller 是一个回调函数。 如果您在代码中省略了caller,那么myFunction()() 将是正确的调用,不会引发错误,只需调用firstFuctionsecondFunction

但是回调的重点是将firstFuctionsecondFunction的结果传递给函数caller。 您必须自己提供该功能,例如alert 函数。

myFunction()(alert) 调用内部函数,将alert 传递给它。 内部函数调用firstFuctionsecondFunction 并将它们的结果传递给caller,我们指定为alert


如果我将其称为myFunction(),您能告诉我它是如何工作的吗?

很简单,这里什么都没有发生。 myFunction 返回一个函数,仅此而已;该函数不再使用,因此被丢弃。

此代码中没有展示高阶函数的一个关键方面:封装值。 考虑这段代码:

const add = (a) => {
  let count = 0;

  return (b) => {
    ++count;
    console.log(`The function to add ${a} to something else has been called ${count} time(s).`);

    return a + b
  };
};

addb 的工作方式与之前的 myFunctioncaller 类似,但现在,外部函数也需要一个参数,所以 add() 将无济于事。 add(5) 调用add 函数并将参数a 设置为5。 此外,add(5)(3) 调用内部返回函数,将b 设置为3 并返回8

add 内部也有一些状态,以名为count 的变量的形式。

const addFive = add(5);

console.log(addFive(3)); // Prints 8.
// But also logs: "The function to add 5 to something else has been called 1 time(s)."

console.log(addFive(10)); // Prints 15.
// But also logs: "The function to add 5 to something else has been called 2 time(s)."

count 只能在outer 内部访问(包括其中的任何函数);它是“封装的”。 这是隐藏某些只能在特定函数内部访问的状态的常用方法。

a其实也封装在add(里面...); acount 具有相同的作用域并且彼此没有太大区别,只是 a 可以在调用 add 时设置为参数,而 count 不能。


然后您添加了更多上下文,调用看起来更像这样:

someOtherFunction(myFunction())

这与之前您刚刚编写诸如myFunction() 之类的调用的情况不同。 现在,结果被丢弃,因为它被输入someOtherFunction(在原始代码中:store.dispatch)。

[caller] 没有作为参数传递,也没有在任何地方定义。

但是现在someOtherFunction 的工作是使用适当的回调函数作为参数调用myFunction()。 现在,不要自己传递函数;相反,someOtherFunction 会为您完成。

将此与Where do the parameters in a JavaScript callback function come from? 进行比较。 如果您回到前面的 sum 示例函数,那么您可以自己传递参数:

sum(1, 2);

或者一些库或框架为你做:

// Library code
const doTheRightThing = (callback) => callback(1, 2);
// Your code
doTheRightThing(sum);

或内置(或主机定义)函数为您完成:

[ 1, 2, 3 ].reduce(sum, 0);

这些参数来自于哪里或从哪里调用函数并不重要。 但是它被调用了,并且它是用正确的参数调用的somewhere


1:高阶函数的两个定义是

  1. 返回函数的函数,或
  2. 一个将另一个函数作为参数的函数。

outer 符合第一个定义。 outer()(独立且巧合地)符合第二个定义。

2:好吧,当然,sum() 也不会抛出错误,但 ab 将分别是 undefinedundefined + undefined 具有不会导致错误的语义,但希望您明白这一点:在您的原始代码中,myFunction()() 只会抛出错误,因为undefined(firstFunction()) 是不可能的。罪魁祸首是一样的:callerundefined

【讨论】:

  • 感谢 Sebastian 的详尽解释。我经历了你的观点,我正在得到你的观点。例如。关于你的解释“但回调的重点是将 firstFuction 和 secondFunction 的结果传递给函数调用者。你必须自己提供该函数,例如警报函数。)”问题是,没有地方定义那个调用者做。我添加了整个代码,我不明白的是 dispatch 参数对我来说没有意义......
  • @VaclavVlcek 阅读编辑后的答案(在“You've then added more context [...]”之后)。
【解决方案3】:

我认为您还没有打印整个片段。因为,myFunction() 内部没有 firstFunction()secondFunction() 的定义。

回答您的问题,如果您想调用myFunction() 中定义的函数,您需要在返回函数时将值赋给一个新变量。然后简单地调用函数。

const myFunctionResult = myFunction();
myFunctionResult(someOtherCallerFunction);

但请注意,由于上述原因,这会引发错误。

【讨论】:

  • “您需要将值分配给新变量” — 不,您不需要“但请注意,由于上述原因,这会引发错误。” — OP已经对代码的工作方式感到困惑;为什么不向他们展示如何正确拨打电话?
  • 你是对的,你可以诉诸链接,但看起来可能会让他们感到困惑,这就是我诉诸长解决方案的原因。根据他们提出的问题没有目标,所以我只是回答了他们是什么阻碍了他们。问题很明显,您是对的,我们可以进一步解释。
猜你喜欢
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 2013-06-21
  • 1970-01-01
  • 2020-04-09
  • 2021-12-13
相关资源
最近更新 更多