Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.javascript > #14746 > unrolled thread
| Started by | Mason Barge <masonbarge@gmail.com> |
|---|---|
| First post | 2012-06-28 09:49 -0400 |
| Last post | 2012-06-30 15:42 +0200 |
| Articles | 10 — 6 participants |
Back to article view | Back to comp.lang.javascript
Question about max() Mason Barge <masonbarge@gmail.com> - 2012-06-28 09:49 -0400
Re: Question about max() Mike Winter <mlwinter@gmail.com> - 2012-06-28 07:08 -0700
Re: Question about max() Stefan Weiss <krewecherl@gmail.com> - 2012-06-28 16:35 +0200
Re: Question about max() Mason Barge <masonbarge@gmail.com> - 2012-06-28 13:19 -0400
Re: Question about max() Stefan Weiss <krewecherl@gmail.com> - 2012-06-28 20:00 +0200
Re: Question about max() Mason Barge <masonbarge@gmail.com> - 2012-06-29 10:48 -0400
Re: Question about max() Gene Wirchenko <genew@ocis.net> - 2012-06-29 10:09 -0700
Re: Question about max() Stefan Weiss <krewecherl@gmail.com> - 2012-06-29 21:18 +0200
Re: Question about max() Scott Sauyet <scott.sauyet@gmail.com> - 2012-06-29 12:55 -0700
Re: Question about max() Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2012-06-30 15:42 +0200
| From | Mason Barge <masonbarge@gmail.com> |
|---|---|
| Date | 2012-06-28 09:49 -0400 |
| Subject | Question about max() |
| Message-ID | <njnou7tungmkib8ip6tbbq7oqufah4d027@4ax.com> |
I encountered the following line of code in a lesson from a "learn
javascript" tbook:
**
var sorted = words.map (function(value) {return
value.toLowerCase();}).sort();
**
I get all of this except for one thing. How or from where does the
parameter of the anonymous function get its argument?
There is no mention of a "var value" and by experimentation any variable
name works, so it has nothing to do with the frequently encountered
property "value" (and afaik using the word "value" for the parameter was
not a good choice of words, even though it isn't reserved).
So perhaps a better question would be, where/how does "foo" get its value
in:
**
var sorted = words.map (function(foo) {return
value.toLowerCase();}).sort();
**
TYIA
This is driving me nuts.
Here is the entire js file:
**
function $(id) {
'use strict';
if (typeof id !== 'undefined') {
return document.getElementById(id);
}
}
function setText (elementId, message) {
'use strict';
if ((typeof elementId == 'string') && (typeof message ==
'string')) {
var output = $(elementId);
output.textContent = message;
}
}
function sortWords (max) {
'use strict';
var words = $('words').value;
words = words.split (' ');
var sorted = words.map (function(rixx) {alert(rixx); return
rixx.toLowerCase();}).sort();
setText ('output', sorted.join(', '));
return false;
}
function init() {
'use strict';
$('theForm').onsubmit = sortWords;
}
window.onload = init;
**
--
Mason
"Let no man pull you low enough to hate him."
~ Martin Luther King
[toc] | [next] | [standalone]
| From | Mike Winter <mlwinter@gmail.com> |
|---|---|
| Date | 2012-06-28 07:08 -0700 |
| Message-ID | <9a27b5c3-2e16-4644-8c6b-6cb1ea102653@googlegroups.com> |
| In reply to | #14746 |
On Thursday, 28 June 2012 14:49:20 UTC+1, Mason Barge wrote:
> I encountered the following line of code in a lesson from a "learn
> javascript" tbook:
>
> **
> var sorted = words.map (function(value) {return
> value.toLowerCase();}).sort();
> **
>
> I get all of this except for one thing. How or from where does the
> parameter of the anonymous function get its argument?
> [snip]
That function is a callback for the Array.prototype.map method. The method calls it once for each element and uses the return value to create elements in a new array.
The callback actually receives three arguments. You can read more at <https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map>
Kind regards,
Mike
[toc] | [prev] | [next] | [standalone]
| From | Stefan Weiss <krewecherl@gmail.com> |
|---|---|
| Date | 2012-06-28 16:35 +0200 |
| Message-ID | <jshq3n$m1k$1@news.albasani.net> |
| In reply to | #14746 |
On 2012-06-28 15:49, Mason Barge wrote:
> var sorted = words.map (function(value) {return
> value.toLowerCase();}).sort();
> **
>
> I get all of this except for one thing. How or from where does the
> parameter of the anonymous function get its argument?
>
> There is no mention of a "var value" and by experimentation any variable
> name works, so it has nothing to do with the frequently encountered
> property "value" (and afaik using the word "value" for the parameter was
> not a good choice of words, even though it isn't reserved).
You have to look at which parameters the map() function uses. map() is a
property of Array.prototype, and the ECMAScript 5 specification defines
it as:
| 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
|
| callbackfn should be a function that accepts three arguments. map
| calls callbackfn once for each element in the array, in ascending
| order, and constructs a new Array from the results.
| [...]
| callbackfn is called with three arguments: the value of the element,
| the index of the element, and the object being traversed.
So that's what the first parameter is used for - the value of the
current array element (*). That it's named "value" in your example is a
user choice (made by the book author). You could name it (almost)
anything you want and its value would be the same. Parameter names are
only of significance inside the function body. It's their position that
counts.
> So perhaps a better question would be, where/how does "foo" get its value
> in:
>
> **
> var sorted = words.map (function(foo) {return
> value.toLowerCase();}).sort();
Same answer, but you have to do it consistently. In this example, you
should have written foo.toLowerCase().
- stefan
*) The other two arguments aren't assigned to named parameters, but the
values are still available in the "arguments" object.
[toc] | [prev] | [next] | [standalone]
| From | Mason Barge <masonbarge@gmail.com> |
|---|---|
| Date | 2012-06-28 13:19 -0400 |
| Message-ID | <di3pu7lo5mjq22e92lsk17m37mhk3qoa90@4ax.com> |
| In reply to | #14750 |
On Thu, 28 Jun 2012 16:35:35 +0200, Stefan Weiss <krewecherl@gmail.com>
wrote:
>On 2012-06-28 15:49, Mason Barge wrote:
>> var sorted = words.map (function(value) {return
>> value.toLowerCase();}).sort();
>> **
>>
>> I get all of this except for one thing. How or from where does the
>> parameter of the anonymous function get its argument?
>>
>> There is no mention of a "var value" and by experimentation any variable
>> name works, so it has nothing to do with the frequently encountered
>> property "value" (and afaik using the word "value" for the parameter was
>> not a good choice of words, even though it isn't reserved).
>
>You have to look at which parameters the map() function uses. map() is a
>property of Array.prototype, and the ECMAScript 5 specification defines
>it as:
>
>| 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
>|
>| callbackfn should be a function that accepts three arguments. map
>| calls callbackfn once for each element in the array, in ascending
>| order, and constructs a new Array from the results.
>| [...]
>| callbackfn is called with three arguments: the value of the element,
>| the index of the element, and the object being traversed.
>
>So that's what the first parameter is used for - the value of the
>current array element (*). That it's named "value" in your example is a
>user choice (made by the book author). You could name it (almost)
>anything you want and its value would be the same. Parameter names are
>only of significance inside the function body. It's their position that
>counts.
Okay, I get all that and I understand how to use it. What is perplexing
is that, unlike a normal function parameter, the first parameter in the
callback function is never explicitly assigned a value in the code.
In other words, there is no "value =" expression anywhere. If I did this
as a loop, at some point there would be something like "value = i" or
"value = array[i]" in the code.
So (I assume) map() must silently assign it a value equal to the current
array item. Am I completely off track?
Is this a common feature of callback functions?
[toc] | [prev] | [next] | [standalone]
| From | Stefan Weiss <krewecherl@gmail.com> |
|---|---|
| Date | 2012-06-28 20:00 +0200 |
| Message-ID | <jsi63d$fqc$1@news.albasani.net> |
| In reply to | #14753 |
On 2012-06-28 19:19, Mason Barge wrote:
> Okay, I get all that and I understand how to use it. What is perplexing
> is that, unlike a normal function parameter, the first parameter in the
> callback function is never explicitly assigned a value in the code.
>
> In other words, there is no "value =" expression anywhere. If I did this
> as a loop, at some point there would be something like "value = i" or
> "value = array[i]" in the code.
>
> So (I assume) map() must silently assign it a value equal to the current
> array item. Am I completely off track?
>
> Is this a common feature of callback functions?
It's a common feature of all function calls.
Look at this trivial case:
function test(val) {
alert(val);
}
test(42);
There's no "val=42" assignment here, either, because the argument value
is assigned based on parameter position.
The map() example is a little harder to understand, because you can't
see the code inside the map() function. But you can build your own map()
function, which should make things clearer:
// (simplified, see below)
Array.prototype.myMap = function (callback) {
// note: "this" is the "even" array below (in this example)
var result = [];
for (var i in this) {
result[i] = callback(this[i], i, this);
}
return result;
};
var even = [2, 4, 6, 8];
var odd = even.myMap(function (value) { return value + 1; });
alert(odd.join(", ")); // 3, 5, 7, 9
Now you can see how the callback function is called with three
parameters, the first being the value of the current array element.
Still no "value=" assignment, because none is needed.
Note that I left out the thisArg parameter and a number of checks to
make the example simpler. If you're interested, you can find a more
complete implementation on the MDN site:
<https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map>
- stefan
[toc] | [prev] | [next] | [standalone]
| From | Mason Barge <masonbarge@gmail.com> |
|---|---|
| Date | 2012-06-29 10:48 -0400 |
| Message-ID | <treru7df1q1bq27ru0hfhobo4eo42vels5@4ax.com> |
| In reply to | #14754 |
On Thu, 28 Jun 2012 20:00:12 +0200, Stefan Weiss <krewecherl@gmail.com>
wrote:
>On 2012-06-28 19:19, Mason Barge wrote:
>> Okay, I get all that and I understand how to use it. What is perplexing
>> is that, unlike a normal function parameter, the first parameter in the
>> callback function is never explicitly assigned a value in the code.
>>
>> In other words, there is no "value =" expression anywhere. If I did this
>> as a loop, at some point there would be something like "value = i" or
>> "value = array[i]" in the code.
>>
>> So (I assume) map() must silently assign it a value equal to the current
>> array item. Am I completely off track?
>>
>> Is this a common feature of callback functions?
>
>It's a common feature of all function calls.
>Look at this trivial case:
>
> function test(val) {
> alert(val);
> }
> test(42);
>
>There's no "val=42" assignment here, either, because the argument value
>is assigned based on parameter position.
I understand that one, LOL, but it doesn't really apply to my conundrum
here because test() will return undefined.
>The map() example is a little harder to understand, because you can't
>see the code inside the map() function. But you can build your own map()
>function, which should make things clearer:
>
> // (simplified, see below)
> Array.prototype.myMap = function (callback) {
> // note: "this" is the "even" array below (in this example)
> var result = [];
> for (var i in this) {
> result[i] = callback(this[i], i, this);
> }
> return result;
> };
>
> var even = [2, 4, 6, 8];
>
> var odd = even.myMap(function (value) { return value + 1; });
>
> alert(odd.join(", ")); // 3, 5, 7, 9
That's ever so helpful, thanks. I'm not at a point where I can actually
understand your example. I have to imagine that I'm not the only person
in the world who gets to have a bit of fun figuring out callback
functions.
I'm just going to assume, for the time being, that it is somehow possible
for a method to reach up into its parent object and parse the value(s) of
a property, including an array property.
[toc] | [prev] | [next] | [standalone]
| From | Gene Wirchenko <genew@ocis.net> |
|---|---|
| Date | 2012-06-29 10:09 -0700 |
| Message-ID | <u7oru7pnjancbla6s05jrc0b7lgcp0imnq@4ax.com> |
| In reply to | #14765 |
On Fri, 29 Jun 2012 10:48:48 -0400, Mason Barge <masonbarge@gmail.com>
wrote:
[snip]
>I'm just going to assume, for the time being, that it is somehow possible
>for a method to reach up into its parent object and parse the value(s) of
>a property, including an array property.
No need. The arguments passed are available through the
arguments property.
Sincerely,
Gene Wirchenko
[toc] | [prev] | [next] | [standalone]
| From | Stefan Weiss <krewecherl@gmail.com> |
|---|---|
| Date | 2012-06-29 21:18 +0200 |
| Message-ID | <jskv2t$mst$1@news.albasani.net> |
| In reply to | #14765 |
On 2012-06-29 16:48, Mason Barge wrote:
> On Thu, 28 Jun 2012 20:00:12 +0200, Stefan Weiss <krewecherl@gmail.com>
> wrote:
>> Array.prototype.myMap = function (callback) {
>> // note: "this" is the "even" array below (in this example)
>> var result = [];
>> for (var i in this) {
>> result[i] = callback(this[i], i, this);
>> }
>> return result;
>> };
>>
>> var even = [2, 4, 6, 8];
>>
>> var odd = even.myMap(function (value) { return value + 1; });
>>
>> alert(odd.join(", ")); // 3, 5, 7, 9
> I'm just going to assume, for the time being, that it is somehow possible
> for a method to reach up into its parent object and parse the value(s) of
> a property, including an array property.
I don't think it's a good idea to assume that, because that's not at all
how it works. I'll try again:
The key is the map() function, not the callback. Another way to describe
what's happening when you write myArr.map(callbackFunc) is that the
array calls its own map() method. If you're a little familiar with
object-oriented programming, you won't be surprised that the map()
method has access to the array elements, because it "belongs" to the
array object, too (in a manner of speaking). It then iterates over the
elements and calls the callback function once for each element. Each
time, it passes three arguments to the callback function, and uses
whatever the callback returns to build a new array. At the end, it
returns the new array.
Maybe it's easier to see if we don't use an anonymous function:
function increment(value) {
return value + 1;
}
var even = [2, 4, 6, 8];
var odd = even.map(increment);
When map() is called, it performs these steps:
- create a new empty array:
var result = [];
- call the callback for the first element (index 0):
result[0] = increment(2, 0, even); // returns 3
// result is now [3]
- call the callback for the second element (index 1):
result[1] = increment(4, 1, even); // returns 5
// result is now [3, 5]
- call the callback for the third element (index 2):
result[2] = increment(6, 2, even); // returns 7
// result is now [3, 5, 7]
- call the callback for the last element (index 3):
result[3] = increment(8, 3, even); // returns 9
// result is now [3, 5, 7, 9]
- return the result
After map() returns, the result is assigned to the "odd" variable.
(In your original example, the result is not assigned to a variable.
Instead, the intermediate result (also an Array object) has its sort()
method called)
Even though map() calls increment() with three arguments, increment()
only uses the first one. In contrast with other languages, it's fine in
JS to call a function with three arguments, even if only one is listed
in the function declaration.
Hope that helps.
- stefan
[toc] | [prev] | [next] | [standalone]
| From | Scott Sauyet <scott.sauyet@gmail.com> |
|---|---|
| Date | 2012-06-29 12:55 -0700 |
| Message-ID | <32ee8f35-1b37-4b9e-acd2-54fa21a41d76@x39g2000yqx.googlegroups.com> |
| In reply to | #14746 |
Mason Barge wrote:
| var sorted = words.map (function(value) {return
| value.toLowerCase();}).sort();
> I get all of this except for one thing. How or from where does the
> parameter of the anonymous function get its argument?
I'm going to try a slightly different tack than Stefan's explanation.
What Stefan has said is spot-on, but if that is not working for you,
perhaps this approach might be useful.
Imagine that some library had defined a function, `applyToEach`. You
don't have the code available, but you know that it performs this
operation: "given an array and a function, it returns a new array
formed by applying the function to each individual element of the
array." You are given these examples:
var arr1 = [2, 3, 5, 7, 11];
var fn1 = function(x) {return x * x;}
applyToEach(arr1, fn1); // [4, 9, 25, 49, 121]
var arr2 = ["Doha International", "Rayban", "Meebo"];
var fn2 = function(nm) {return nm.substring(0, 3).toLowerCase();};
applyToEach(arr2, fn2) // ["doh", "ray", "mee"]
A question about how the parameter `x` in the first example is
assigned the values of 2, 3, 5, 7, or 11, or how the parameter `nm` in
the second is assigned the values "Doha International", "Rayban", or
"Meebo" can now be explained with this possible implementation:
var applyToEach = function(arr, fn) {
var output = [], i, len;
for (i = 0, len = arr.len; i < len; i++) {
output[i] = fn(arr[i]); // (*)
}
return output[i];
}
In the line marked "(*)", the library is calling the callback function
(`fn`) and passing the current element of the array. The library is
not assigning the variable name to be used. The function you supply
does that (`x` in one case, `nm` in another.)
Your original question deals with `map`, which is quite similar. It
is defined to do much the same thing. But instead of being supplied
an array as a separate parameter, is is a method of the array
prototype. So `this` is the array. (It is further complicated by the
fact that the callback function is actually supplied additional
parameters, but they are irrelevant to the current discussion.)
So `map` is a method of the Array prototype which accepts a function
and returns a new array formed by running that function against each
element of the current array. With that in mind, you might look again
at Stefan's simplified version and see if it makes sense.
Does that help?
-- Scott
[toc] | [prev] | [next] | [standalone]
| From | Thomas 'PointedEars' Lahn <PointedEars@web.de> |
|---|---|
| Date | 2012-06-30 15:42 +0200 |
| Message-ID | <1917299.FpnORuNF1W@PointedEars.de> |
| In reply to | #14746 |
[All good things …]
Mason Barge wrote:
> I encountered the following line of code in a lesson from a "learn
> javascript" tbook:
Apparently not a good book if you are asking about basics here. Or is the
student the problem?
> **
> var sorted = words.map (function(value) {return
> value.toLowerCase();}).sort();
> **
>
> I get all of this except for one thing. How or from where does the
> parameter of the anonymous function get its argument?
The question you wanted to ask was "How or from where does the parameter of
the anonymous function get its value?" (A function has *parameters*. The
values passed to it are the function's *arguments*; they may be assigned to
declared parameters. [FTR: I did get the "parameters" part wrong until not
so long ago, but the 5.1 Edition of ECMAScript – at least – is definitive
about the terminology there.)
The correct answer to that question is "The parameter gets its value from
the argument that is passed to the anonymous function when that function is
called by map().
Think of map() being defined as follows, which should be self-explanatory
(the algorithm is specified in ECMAScript, this is a possible implementation
of it, from JSX:object.js):
Array.prototype.map = function (callback, oThis) {
var jsx_object = jsx.object;
if (!jsx_object.isMethod(callback))
{
jsx.throwThis("TypeError",
(jsx_object.isMethod(callback, "toSource")
? callback.toSource()
: callback)
+ " is not callable",
this + ".map");
}
var
len = this.length >>> 0,
res = [];
for (var i = 0; i < len; i++)
{
res[i] = callback.call(oThis, this[i], i, this);
}
return res;
};
Array instances inherit from Array.prototype through the prototype chain,
and `words' is an Array instance. So it inherits the map() method from
Array.prototype, too.
The anonymous function passed to map()¹ is defined *before* but called *by
map()* *later*. That is,
var sorted = words.map(function (value) {
return value.toLowerCase();
}).sort();
is functionally equivalent to
var foo = function (value) {
return value.toLowerCase();
};
var sorted = words.map(foo).sort();
This pattern is called a *callback*, which originates in functional
programming and is very useful (cf. currying, event listeners). ECMAScript
implementations are one of the more interesting programming languages
because they combine several programming paradigms: procedural-imperative,
object-oriented and functional programming. (This can also be found in PHP
and Python, and to a lesser extent in Java.)
> There is no mention of a "var value"
Why should it? The parameter of the function is declared in the formal
parameter list already; the declared name is `value'.
> and by experimentation any variable name works,
_Not_ *any* name; it must be an *identifier*. That aside: Of course.
Functions often have parameters because they are written to avoid repetitive
programming of similar tasks. How a function parameter is named is of
secondary importance, as it is *local* to the function code only.
Programming 101.
> so it has nothing to do with the frequently encountered property "value"
Correct.
> (and afaik using the word "value" for the parameter was
> not a good choice of words, even though it isn't reserved).
Only if it is not the only named parameter. In this case, the value of the
array element is passed, so `value' certainly is an appropriate name.
However, if this bothers you, you may name the parameter `element'. You may
also name it `supercalifragilisticexpialigocious' as long as you use the
same identifier with the same meaning in the function code, but that would
be harder to maintain and a bit silly on top of that.
PointedEars
___________
¹ For simplicity, I am saying "function" here when it would be exact to say
"reference to a Function instance" etc. Objects have identity, not name.
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$8300dec7@news.demon.co.uk>
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.javascript
csiph-web