0

I understand the following codes:

function subtract(p1, p2) { return p1 - p2; }
function calculate(p1, p2, calculator) { return calculator(p1, p2); }
var result = calculate(2, 3, subtract); //-1

But I don't understand the following codes about how p1 and p2 can be recognized as 2 and 3:

var result = calculate(2, 3, function (p1, p2) { return p1 - p2; }); //-1
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user1386284
  • 95
  • 1
  • 7

4 Answers4

2

UPDATE to address comment:

May be my question was not clear enough. Let me rephrase it at the other way. If the Calculate has 5 arguments: function calculate(p1, p2, p3, p4, calculator) { return calculator(p1, p2); } Will it be possible to be called like: var result = calculate(2, 3, 9, 5, function (p4, p3) { return p3 - p4; }); //4 not -1 My actual question is: how can the one of parameters uses other parameters as its arguments and distinguish which parameter should become which argument?

The reason that the anonymous function knows to use the p1 and p2 arguments is because the parameters from the calculate function definition are what is passed into the anonymous function as arguments in the body of calculate.

Perhaps a clearer way to understand what is happening here is to change the name of the parameters in the function definition of the anonymous function so that those same parameter names aren't used in the function definition of your anonymous function. This creates a lot of confusion and is often done purposely in Computer Science textbooks when teaching about principles of programming languages and abstractions.

In addition to changing the parameter names in the anonymous function, changing the calculate parameter "calculator" to the parameter name "fn" may also help avoid confusion between calculate and calculator. Consider your function:

Function Definition:

function calculate(p1, p2, p3, p4, fn) {

    // here you can see that calculate's p1 and p2 arguments (above) are bound
     // to whatever function we pass into the fn parameter.
      // Whatever values p1 and p2 represent are passed into the function "fn"
       // and are assigned to the parameters p1 and p2 of fn.
    return fn(p1, p2); 
}

Function call calculate, with anonymous function:

How do we know which of the first 4 arguments get passed into the anonymous function vs which arguments aren't used?

// so here, we pass in an anonymous function that takes 2 parameters, one 
 //and two. We know from the definition above that whatever values are
  // passed in as calculate's p1 and p2 parameters are passed into fn, 
   //so one and two are assigned the values 2 and 3 respectively,
    // since p1 and p2 were assigned 2 and 3, and p1 and p2 were passed into
     // fn in calculate's function definition above.
calculate(2, 3, 9, 5, function(one, two) {
    return two - one;
});


// We know the values assigned to p1 and p2 are passed into the fn function
 // as the parameters of the anonymous function known as one and two.      
var result = calculate(2, 3, 9, 5, function(2, 3) {
    return 3 - 2;
}

See return fn(p1, p2); in the function definition for calculate, at the top of this answer, as a reminder of why the values assigned to p1 and p2 are passed into the anonymous function.

Thus, the anonymous function returns 3 - 2 = 1.

For further reading on how this concept can create abstractions, see Joel Spolsky's article - Can Your Programming Language Do This? Joel does a great job of explaining why JavaScript is so awesome!

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
  • May be my question was not clear enough. Let me rephrase it at the other way. If the Calculate has 5 arguments: function calculate(p1, p2, p3, p4, calculator) { return calculator(p1, p2); } Will it be possible to be called like: var result = calculate(2, 3, 9, 5, function (p4, p3) { return p3 - p4; }); //4 not -1 Note: the first parameter in the anonymous function is p4 not p3; likewise for the 2nd parameter. My actual question is: how can the one of parameters uses other parameters as its arguments and distinguish which parameter should become which argument? – user1386284 May 12 '12 at 08:32
  • The answer is too long-winded for this simple problem and inaccurate on top of that. The function isn't being passed; an object is created, and a reference to it is passed. Overlooking that, you're saying "parameters as arguments" -- confusing at best. Functions have *arguments*, and function calls have an *argument list*; "parameter" isn't used in these languages. You go on saying the function is "passed by reference"; it isn't: object references are values, and *all* arguments are passed *by value*. Not "parameters are replaced"; arguments are initialized. I could go on, but no space left. – PointedEars May 12 '12 at 16:35
  • @PointedEars - You're right about it being too long and about some of the ambiguous information. I narrowed it down a bit, removing the parts the op was confused about, and I cleaned up the parts I added recently. You're also correct about "pass by value" for JavaScript. With that said, I'm not 100% sure I agree that "parameter" isn't used in these languages (although that doesn't mean I'm not using them incorrectly). See http://stackoverflow.com/questions/3176310/difference-between-parameter-and-argument for more information. Thanks again for helping me improve my answer! :) – jamesmortensen May 12 '12 at 18:49
  • @jmort253 Thanks for doubting me on "parameter". I stand corrected. I have checked the Editions of the ECMAScript Language Specification (which I think are the only reliable source for unambiguous terminology here), and it appears that the term "parameter" *is* used for the declared name (like, a `FunctionDeclaration` production contains an optional `FormalParameterList` goal symbol), while "argument" is used for the values in the function call (like, a `CallExpression` production contains – in effect – an optional `ArgumentList` goal symbol; the `arguments` object). – PointedEars May 13 '12 at 00:48
  • @PointedEars - Consistency is important. Thank you for following up with that information. You've taught me something that will help make it easier for me to communicate with others when talking about code. I discovered that I was mistakenly using parameters and arguments interchangeably until today. – jamesmortensen May 13 '12 at 00:52
0

Well the function is getting called from calculate with reference name calculator and It looks confusing to you because the arg names in the calculate and the arg names in the calculator is same.

var result = calculate(2, 3, function (p1, p2) { return p1 - p2; }); //-1

Now the calculate function,

function calculate(p1, p2, calculator) { return calculator(p1, p2); }

The args are as follows,

p1- 2
p2- 3
calculator - function (p1, p2) { return p1-p2; }
Selvakumar Arumugam
  • 79,297
  • 15
  • 120
  • 134
0

calculate calls it's third argument ( calculator ) as a function with its own first two arguments, p1, and p2.

                   //parameters                            // passing trough as arguments
function calculate(p1, p2, calculator) { return calculator(p1, p2); }

Calculator has access to p1 and p2 from calculate trough it's own version of p1 and p2, this may look confusing because the parameters happen to have the same name, but they are just being passed trough.

In your example you pass a local function to calculate, which will then be called with the first 2 parameters from calculate.

Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
0

The third argument is an (anonymous) function expression. The result of that expression is a reference to a newly created callable object, a Function instance.

The calculate function is passed 2, 3, and that object reference as arguments. The first and second parameters of calculate are named p1 and p2, respectively. The third parameter is named calculator. Therefore, in calculate, p1 is initialized with 2, p2 is initialized with 3, and calculator is initialized with a reference to the previously created Function instance.

calculate then calls the referred Function instance, passing the current values of its p1 and p2 parameters, 2 and 3, as first and second argument.

The called anonymous function has first and second parameters named p1 and p2 of its own, which then assume the values passed for them by the arguments in the call, 2 and 3. An expression referring to the parameters is evaluated and the result returned to its caller, which is calculate.

calculate then returns that value to its caller.

This approach is called a callback.

Next time, please use a debugger first.

PointedEars
  • 14,752
  • 4
  • 34
  • 33
  • `But I don't understand the following codes about how p1 and p2 can be recognized as 2 and 3`: @PointedEars, you did well up until the very last sentence. The op already *knew* the code was working. He was asking someone to explain *why* it works. Is there something I don't know about that I missed? Do debuggers somehow describe, in plain laymens-terms English, what a lambda function is or what an anonymous function is? Do debuggers have secret video tutorials describing callback function concepts? Consider editing that last line out so you don't deface an otherwise great answer. – jamesmortensen May 10 '12 at 14:17
  • @jmort253 Any decent debugger, and certainly those [*built into more recent browsers*](http://www.andismith.com/blog/2011/11/25-dev-tool-secrets/) (with the exception of Firefox, you have to install the Firebug extension there) lets you step into function calls and has a way to show the values of local variables as you are stepping through the code. That shows you *exactly* what is going on here. A bit of basic programming knowledge ("What is a 'local variable'?") is required, of course. – PointedEars May 10 '12 at 22:49
  • I do agree to some extent and have suggested that users learn to use their debugger when extremely basic questions are asked here. With that said, even modern debuggers with step into and step over functionality don't explain a concept, such as closures and anonymous functions, like a human being could, in laymens terms. Sometimes, people just need someone to help get them past a tough concept by explaining conceptually. Consider this, how do we know the op didn't try using a debugger first -- and afterwards -- *still* wasn't clear how or why the code was working? – jamesmortensen May 10 '12 at 23:27
  • @jmort253 You've got to be kidding me. Not using a debugger is just the tip of the iceberg here. How do we know the OP didn't use a debugger? Because they *didn't tell* they did. How do we know the OP didn't have lovesickness? If you're allowing for such assumptions, you make allowances for all kinds of laziness. The concepts here aren't as difficult as you depict them to be. Closures and anonymous functions don't matter here. Functions as first-class objects matter, and are described in JS tutorials *galore*. [SO is *not* a programming tutorial.](http://stackoverflow.com/questions/how-to-ask) – PointedEars May 11 '12 at 10:09
  • May be my question was not clear enough. Let me rephrase it at the other way. If the Calculate has 5 arguments: function calculate(p1, p2, p3, p4, calculator) { return calculator(p1, p2); } Will it be possible to be called like: var result = calculate(2, 3, 9, 5, function (p4, p3) { return p3 - p4; }); //4 not -1 Note: the first parameter in the anonymous function is p4 not p3; likewise for the 2nd parameter. My actual question is: how can the one of parameters uses other parameters as its arguments and distinguish which parameter should become which argument? – user1386284 May 12 '12 at 08:31
  • @user1386284 Yes, but the result will not be what you are expecting. It is all about scope. The names of the arguments of one function are local to that function; they have nothing to do with the names of the arguments of another function. (Names are not even required.) Only the *values* that you pass to each function count. Have you read a JavaScript tutorial yet? Have you tried to use a debugger yet? Putting the statements of function bodies on *separate* lines and inspecting the values of the arguments will help you to see what is going on while stepping through the code (use a breakpoint). – PointedEars May 12 '12 at 16:06
  • 1
    @user1386284 To summarize: It does not matter if the arguments of the callback are named `p4` and `p3`, or `foo` and `bar`. The result will be `1` (`3-2`) as long as the values `2` and `3` are passed to the callback in that order and are used in the arithmetic expression the same way. – PointedEars May 12 '12 at 16:20
  • @PointerEars, thank you for your excellent explanation. Let me revise the caller function a little bit here: var result = calculate(2, 3, 9, 5, function (p4, p2, p3) { return (p3 - p4)/p2; }); Question: since the anonymous function's arguments are passed by reference, not by value, in the mess order and not utilize the existing subtract function but the newly created function, how can it discern if 9 and 5 got passed, not the others? – user1386284 May 13 '12 at 22:15
  • @user1386284 It's _PointedEars_, and AISB arguments are always passed by value. You can discern if `9` and `5` got passed if you *finally* use a debugger, step through to the relevant line and inspect the then-current parameter values. Or you can call `console.log()` or, as in old times, `window.alert()`. All this can be found in any decent tutorial. Go read it. – PointedEars May 13 '12 at 23:53
  • @PointerEars, in other words, we cannot certain which argument will be passed or ensure p1 won't be passed in by coding this way even though we see p1, p2 and p3 actually got passed in instead of p2, p3 and p4 by stepping in the function through debugger, right? In that case, the way of using p1, p2 as this anonymous function's argument is not a good programming practice, isn't it? – user1386284 May 14 '12 at 18:10
  • @user1386284 No, in other words you have not followed any of the advice that you have been given, and you are still misspelling my nickname. I do not see any reason to continue from here. – PointedEars May 14 '12 at 21:57