简体   繁体   English

如何在另一个函数中获取箭头函数的参数?

[英]How to get parameter of an arrow function inside another function?

I want to create a functions that returns the parameter of another function.我想创建一个返回另一个函数参数的函数。 I know I can use argument or rest operator to access parameter inside a function itself, but how can I get them outside that function?我知道我可以使用argument或休息运算符来访问函数本身内部的参数,但是我怎样才能在该函数之外获取它们呢?

const returnValue = (fn) => {
 //How can I get the parameter of fn? Assume I know its arity.
}

For example:例如:

const foo = 'bar'
const hello = 'world'

const returnFirstArgument = (val1, val2) => val1

const returnArgumentByPosition = (fn, num) => {
//Take fn as input, return one of its parameter by number
}

const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1) //Expect it returns 'world'

What you want isn't possible to do without modifying how returnFirstArgument behaves.如果不修改returnFirstArgument行为方式,就不可能实现您想要的。 Take for example the below piece of code:以下面的一段代码为例:

const x = 1 + 2;
console.log(x); // 3

Before a value is assigned to x , the expression 1 + 2 needs to be evaluated to a value.在将值分配给x ,需要将表达式1 + 2计算为一个值。 In this case 1 + 2 gets evaluated to 3 , so x gets assigned to 3 , that way when we print it, it prints the literal number 3 out in the console.在这种情况下, 1 + 2被评估为3 ,因此x被分配给3 ,这样当我们打印它时,它会在控制台中打印出文字数字3 Since it is now just a number, we can't tell how 3 was derived (it could have come from 0 + 3 , 1 * 3 , etc...).由于它现在只是一个数字,我们无法判断3是如何导出的(它可能来自0 + 31 * 3等......)。

Now take a similar example below:现在举一个类似的例子:

const max = Math.max(1, 2);
console.log(max); // 2

The same idea here applies from above.同样的想法也适用于上面。 First Math.max(1, 2) is evaluated to the value of 2 , which is then assigned to max .首先Math.max(1, 2)被评估为2的值,然后分配给max Again, we have no way of telling how 2 was derived.同样,我们无法说明2是如何导出的。

Now consider a function:现在考虑一个函数:

const add = (x, y) => x + y;
const ans = add(1 + 2, Math.max(1, 2));
console.log(ans); // 5

When we call the function, the function's arguments are first evaluated to values.当我们调用函数时,函数的参数首先被评估为值。 The parameters within the function are then assigned to copies of these values:然后将函数内的参数分配给这些值的副本:

const ans = add(1 + 2, Math.max(1, 2));
//               ^--------^------------- both evaluate first before function is invoked

so the above function call becomes:所以上面的函数调用变成:

const ans = add(3, 2);

As a result, inside the add function, x becomes 3 and y becomes 2 .结果,在 add 函数中, x变为3y变为2 Just like with the above first two examples with variables, we have no way of knowing the 3 came from the expression 1+2 and that 2 came from the function call of Math.max(1, 2) .就像上面带有变量的前两个示例一样,我们无法知道3来自表达式1+2 ,而2来自Math.max(1, 2)的函数调用。

So, relating this back to your original question.因此,将其与您的原始问题联系起来。 Your function call is analogous to the add function call shown above:您的函数调用类似于上面显示的 add 函数调用:

const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1)

just like in the other examples, the arguments passed to the function can't be expressions, so they need to be evaluated first to values.就像在其他示例中一样,传递给函数的参数不能是表达式,因此需要首先将它们计算为值。 returnFirstArgument(foo, hello) is evaluated to a value before the returnArgumentByPosition function is invoked. returnFirstArgument(foo, hello)在调用returnArgumentByPosition函数之前被评估为一个值。 It will evaluate to the string "bar" .它将评估为字符串"bar" This results in fn becoming "bar" inside of your returnArgumentByPosition .这会导致fn成为returnArgumentByPosition "bar" As "bar" is just a string, we again have to way of telling where it came from, and so, won't have access to the function which created it.由于"bar"只是一个字符串,我们再次必须知道它来自哪里,因此无法访问创建它的函数。 As a result, we can't access the second argument of the function, since this information is not retained anywhere.因此,我们无法访问该函数的第二个参数,因为该信息不会保留在任何地方。


One approach to do what you're after is to create a recall function.做您所追求的事情的一种方法是创建recall功能。 The recall function is able to "save" the arguments you passed into it, and then expose them later.召回功能能够“保存”您传递给它的参数,然后在以后公开它们。 Put simply, it wraps your original function but is able to save the arguments and the result of calling your original function:简而言之,它包装了您的原始函数,但能够保存参数和调用原始函数的结果:

 const recall = fn => (...args) => { return { args, result: fn(...args), } }; const add = recall((x, y) => x + y); const getN = ({args}, n) => { return args[n]; } const res = getN(add(1, 2), 1); console.log(res);

The above approach means that add() will return an object.上述方法意味着add()将返回一个对象。 To get the result of calling add, you can use .result .要获取调用 add 的结果,可以使用.result The same idea applies to get the arguments of add() .同样的想法适用于获取add()的参数。 You can use .args on the returned object.您可以在返回的对象上使用.args This way of saving data is fine, however, if you want a more functional approach, you can save the data as arguments to a function:这种保存数据的方式很好,但是,如果您想要更实用的方法,可以将数据保存为函数的参数:

 const recall = fn => (...args) => { return selector => selector( args, // arguments fn(...args) // result ); }; // Selectors const args = args => args; const result = (_, result) => result; const getN = (wrapped, n) => { return wrapped(args)[n]; } const add = recall((x, y) => x + y); const wrappedAns = add(1, 2); const nth = getN(wrappedAns, 1); console.log(nth); // the second argument console.log(wrappedAns(result)); // result of 1 + 2

above, rather than returning an object like we were before, we're instead returning a function of the form:上面,我们不是像以前那样返回一个对象,而是返回一个如下形式的函数:

return selector => selector(args, fn(...args));

here you can see that selector is a function itself which gets passed the arguments as well as the result of calling fn() (ie: your addition function).在这里您可以看到selector本身是一个函数,它传递参数以及调用fn()的结果(即:您的加法函数)。 Above, I have defined two selector functions, one called args and another called result .上面,我定义了两个选择器函数,一个称为args ,另一个称为result If the selector above is the args function then it will be passed args as the first argument, which it then returns.如果上面的selectorargs函数,那么它将作为第一个参数传递args ,然后它返回。 Similarly, if the selector function above is the result function, it will get passed both the args and the result of calling fn , and will return the result the return value of fn(...args) .类似地,如果上面的selector函数是result函数,它将同时传递args和调用fn的结果,并将结果返回fn(...args)的返回值。

Tidying up the above (removing explicit returns etc) and applying it to your example we get the following:整理上述内容(删除显式返回等)并将其应用于您的示例,我们得到以下内容:

 const foo = 'bar'; const hello = 'world'; const recall = fn => (...args) => sel => sel(args, fn(...args)); const returnFirstArgument = recall((val1, val2) => val1); const returnArgumentByPosition = (fn, num) => fn(x => x)[num]; const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1); console.log(returnSecondArgument); // world


Side note (for an approach using combinators):旁注(对于使用组合器的方法):

In functional programming, there is a concept of combinators.在函数式编程中,有一个组合子的概念。 Combinators are functions which can be used as a basis to form other (more useful) functions.组合器是可以用作形成其他(更有用)函数的基础的函数。

One combinator is the identity-function, which simply takes its first argument and returns it:一个组合子是恒等函数,它只接受它的第一个参数并返回它:

const I = x => x;

Another combinator is the K-combinator, which has the following structure:另一个组合子是 K-combinator,其结构如下:

const K = x => y => x;

You may have noticed that the first selector function args is missing an argument.您可能已经注意到第一个选择器函数args缺少一个参数。 This is because JavaScript doesn't require you to enter all the parameters that are passed as arguments into the function definition, instead, you can list only the ones you need.这是因为 JavaScript 不要求您输入作为参数传递到函数定义中的所有参数,相反,您可以仅列出您需要的参数。 If we were to rewrite the args function so that it showed all the arguments that it takes, then it would have the following structure:如果我们重写args函数,让它显示它需要的所有参数,那么它将具有以下结构:

const args = (args, result) => args;

If we curry the arguments of this function, we get:如果我们对这个函数的参数进行柯里化,我们会得到:

const args = args => result => args;

If you compare this function to the K-combinator above, it has the exact same shape.如果将此函数与上面的 K 组合器进行比较,则它具有完全相同的形状。 The K-combinator returns the first curried argument, and ignores the rest, the same applies with our args function. K-combinator 返回第一个柯里化参数,并忽略其余参数,这同样适用于我们的args函数。 So, we can say that args = K .所以,我们可以说args = K

Similarly, we can do a similar thing for the result selector shown above.同样,我们可以对上面显示的result选择器做类似的事情。 First, we can curry the arguments of the results selector:首先,我们可以对结果选择器的参数进行柯里化:

const result = _ => result => result;

Notice that this almost has the same shape as the K combinator, except that we're returning the second argument rather than the first.请注意,这几乎K组合器具有相同的形状,除了我们返回的是第二个参数而不是第一个参数。 If we pass the identify function into the K-combinator like so K(I) , we get the following:如果我们像K(I)一样将识别函数传递给 K 组合器,我们会得到以下结果:

const K = x => y => x;
K(I) returns y => I

As we know that I is x => x , we can rewrite the returned value of y => I in terms of x:我们知道Ix => x ,我们可以根据x => x重写y => I的返回值:

y => I
can be written as...
y => x => x;

We can thenalpha-reduce (change the name of y to _ and x to result ) to get _ => result => result .然后我们可以进行alpha-reduce (将y的名称更改为_并将x的名称更改为result )以获得_ => result => result This now is the exact same result as the curried result function.现在这与柯里化result函数的结果完全相同。 Changing variable names like this is perfectly fine, as they still refer to the same thing once changed.像这样更改变量名是完全没问题的,因为一旦更改,它们仍然指代相同的事物。

So, if we modify how selector is called in the recall function so that it is now curried, we can make use of the I and K combinators:因此,如果我们修改调用函数中selector的调用方式,使其现在柯里化,我们可以使用IK组合子:

 const I = x => x; const K = x => y => x; const recall = fn => (...args) => sel => sel(args)(fn(...args)); const args = K; const result = K(I); const getN = (fn, n) => fn(args)[n]; const add = recall((x, y) => x + y); const addFn = add(1, 2); const nth = getN(addFn, 1); console.log(nth); // the second argument console.log(addFn(result)); // result of 1 + 2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM