Why is the length of the reduce method one - javascript

Why is the length of the reduce method one? Doesn't he have two parameters?
I try to output the length of reduce method in browser and node server. They both show one. What's the reason? Shouldn't they be two?

edited
Function.length seems to return number of only needed arguments (which is mentioned as "typical number of arguments expected by the function" in ECMA)
I checked through other functions like map and filter and they behaved the same, returning 1 for length though they accepts optional arguments.
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/length
https://www.ecma-international.org/ecma-262/6.0/#sec-function-instances-length
As it is said inthe MDN, reduce results in a single output value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
If you are mentioning about something else, it is better to provide example code.

Related

How do I concatenate the results of getElementsByClassName in plain Javascript?

I'm trying to write a bookmarklet that works on two different pages. I can successfully iterate through and process the elements once I get hold of them, but in order to accommodate the two different pages, I wanted to compile a list of DIVs by two different class names. I started with this:
traces=
[].concat.apply(document.getElementsByClassName('fullStacktrace'),
document.getElementsByClassName('msg'))
but on the first page it results this:
> traces=[].concat.apply(document.getElementsByClassName('fullStacktrace'),
document.getElementsByClassName('msg'))
[HTMLCollection[2]]
> traces[0]
[div.fullStacktrace, div.fullStacktrace]
> traces[1]
undefined
> traces[0][0]
<div class=​"fullStacktrace" style=​"height:​ auto;​">​…​</div>​
whereas on the other page
> traces=[].concat.apply(document.getElementsByClassName('fullStacktrace'),
document.getElementsByClassName('msg'))
[HTMLCollection[0], div#msg_2.msg.l1, ... div#msg_6460.msg.l1]
> traces[0]
[]
> traces[1]
<div class=​"msg l1" id=​"msg_2">​…​</div>​
So getElementsByClassName works for both pages, but it looks like concat.apply doesn't iterate its first argument, but does iterate its second argument?!
So I tried using concat twice and going all out with parentheses:
traces=(
(
[].concat.apply(document.getElementsByClassName('fullStacktrace'))
)
.concat.apply(document.getElementsByClassName('msg'))
)
but it gets even stranger: the first page said:
Array[1]
> 0: HTMLCollection[0]
length: 0
> __proto__: HTMLCollection
length: 1
> __proto__: Array[0]
and the other:
Array[1]
> 0: HTMLCollection[283] // Lots of div#msg_3.msg.l1 sort of things in here
length: 1
>__proto__: Array[0]
get its full complement of divs.
Since the two groups of elements are only on one or the other page, I can just use a conditional in my particular case, but the behaviour above is very surprising to me, so I would like to understand it. Any ideas?
For reference, this is on Mac Chrome Version 56.0.2924.87 (64-bit)
To use concat, the collections would need to be arguments that are Arrays. Any other argument won't get flattened. You can use .slice() for this:
traces= [].concat([].slice.call(document.getElementsByClassName('fullStacktrace')),
[].slice.call(document.getElementsByClassName('msg')))
Modern syntax would make it quite a bit nicer. This uses the "spread" syntax to create a new array with the content of both collections distributed into it:
traces = [...document.getElementsByClassName('fullStacktrace'),
...document.getElementsByClassName('msg')]
Or to use something that is more easily polyfilled, you can use Array.from() to convert the collections:
traces = Array.from(document.getElementsByClassName('fullStacktrace'))
.concat(Array.from(document.getElementsByClassName('msg'))))
Overall, I wouldn't use getElementsByClassName in the first place. I'd use .querySelectorAll. You get better browser support and more powerful selection capabilities:
traces = document.querySelectorAll('.fullStacktrace, .msg')
This uses the same selectors that CSS uses, so the above selector is actually passing a group of two selectors, each of which selects the elements with its respective class.
Detailed explanation
First Example:
My explanation of the issue above was too terse. Here's your first attempt:
traces = [].concat.apply(document.getElementsByClassName('fullStacktrace'),
document.getElementsByClassName('msg'))
The way .concat() works is to take whatever values are given as its this value and its arguments, and combine them into a single Array. However, when the this value or any of the arguments are an actual Array, it flattens its content one level into the result. Because an HTMLCollection isn't an Array, it's seen as just any other value to be added, and not flattened.
The .apply() lets you set the this value of the method being called, and then spread the members of its second argument as individual arguments to the method. So given the above example, the HTMLCollection passed as the first argument to .apply() does not get flattened into the result, but the one passed as the second argument to .apply() does, because it's .apply() doing the spreading, not .concat(). From the perspective of .concat(), the second DOM selection never existed; it only saw individual DOM elements that have the msg class.
Second Example:
traces=(
(
[].concat.apply(document.getElementsByClassName('fullStacktrace'))
)
.concat.apply(document.getElementsByClassName('msg'))
)
This is a bit different. This part [].concat.apply(document.getElementsByClassName('fullStacktrace')) suffers from the same problem as the first example, but you noticed that the HTMLCollection doesn't end up in the result. The reason is that you actually abandoned the result of that call when you chained .apply.concat(... to the end.
Take a simpler example. When you do this:
[].concat.apply(["bar"], ["baz", "buz"])
...the [] is actually a wasted Array. It's just a short way to get to the .concat() method. Inside .concat() the ["bar"] will be the this value, and "baz" and "buz" will be passed as individual arguments, since again, .apply() spreads out its second argument as individual arguments to the method.
You can see this more clearly if you change the simple example to this:
["foo"].concat.apply(["bar"], ["baz", "buz"])
...notice that "foo" is not in the result. That's because .concat() has no knowledge of it; it only knows if ["bar"], "baz" and "buz".
So when you did this:
[].concat.apply(collection).concat.apply(collection)
You did the same thing. The second .concat.apply basically drops the first and carries on with only the data provided to .apply(), and so the first collection doesn't appear. If you hadn't used .apply for the second call, you'd have ended up with an array with two unflattened HTMLCollections.
[].concat.apply(collection).concat(collection)
// [HTMLCollection, HTMLCollection]
Because the call to document.getElementByClassName returns a HTMLCollection rather than an Array you're getting the strange results you're seeing. What we can do to combat this is convert the HTMLCollection to an array, and then concat them, like this:
traces= [].slice.call(document.getElementsByClassName('fullStacktrace')).concat([].slice.call(document.getElementsByClassName('msg')))
If browser compatibility is a concern though, have a look at some of the other solutions from here that may help for earlier versions of IE.
Explanation:
Document.getElementsByClassName returns a live HTMLCollection of elements that match the provided class names. This is an array-like object but not an array.
So your initial attempts were trying to merge two live lists of HTML elements rather than merge arrays of elements.
Suggested Solution:
Create a solid (non-live array) using Array#slice() and then you can merge the two arrays directly:
var fullStacktrace = [].slice.call(document.getElementsByClassName('fullStacktrace'), 0),
msg = [].slice.call(document.getElementsByClassName('msg'), 0),
mergedDivs = fullStacktrace.concat(msg);

Property is faster than method? Need reason for it

As I google this question so one person give answer that property is faster than method and give one example of size() and length.
He said length is faster than size because length is property. Can you please let me know is it correct ? Or If you will give example then it will be great for me.
size internally calls the length
//http://code.jquery.com/jquery-latest.js
// The number of elements contained in the matched element set
size: function() {
return this.length;
},
So if you are using length then you are avoiding one extra method call. The Jquery docs says:
The .size() method is functionally equivalent to the .length property;
however, the .length property is preferred because it does not have
the overhead of a function call.
I am assuming that you want to get the length of a String or the number of elements in an Array.
size() is not a method of the Array or String objects. Thus if it exists some library or you yourself have added this method to the respective prototypes. length on the other hand is a default property and (should) exist in any JS runtime.
Unless you cannot use length, the size function will just add unneeded overhead and I would go for the property.
Check the following to links:
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/prototype
If you will read length property then only time required to access an object property will be needed.
However if you will call size() then first of all a function will be called, this function will read length property internally and then return that value to the caller.
You can clearly see that you are doing the same thing in both cases. But if you call the function then it will include time for calling a function + returning that value too..
Length returns the same thing and is slightly faster according to the jQuery documentation.
Source: http://api.jquery.com/size/

Function apply with arguments using slice

Looking at this tutorial I saw the following code suggestion in one comment:
init:function(callback){
var that =this ;
return $http.jsonp(this.url).success(
function(data){
that.availableGenres = that.getGenres(data);
that.results = that.getResults(data);
if(callback)callback.apply(null,[].slice.call(arguments))
}
)
}
But this line callback.apply(null,[].slice.call(arguments)) looks weird to me.
Why not just: callback.apply(null, arguments)? Because I don't like when I don't understand the point of something I played around with this Fiddle to understand the need of slice function in there. But it gives me the same result and I still do not get it.
Anyone knows why slice is needed?
You don't really need it, passing arguments to apply directly is fine.
It is specified to be allowed in EcmaScript 3, and EcmaScript 5 even allows any array-like objects not only arrays and arguments objects. Maybe it was needed to be backwards-compatible with even earlier or buggy JS implementations. Also, (as you see from the other answers), some people don't know about this fact and think they would need to pass actual arrays - so they are unnecessary careful.
slice is needed because function.apply requires an array, arguments is not a real array but can be converted to one using slice.
The arguments object is not an Array. It is similar to an Array, but
does not have any Array properties except length. For example, it does
not have the pop method. However it can be converted to a real Array:
var args = Array.prototype.slice.call(arguments);
this is one of the ugly parts of javascript

why calling apply on Math.max works and without it doens't

If you asked me to get the max of an array, I would just do:
var nums = [66,3,8,213,965,1,453];
Math.max.apply(Math, nums);
of course, I could also do:
nums.sort(function(a, b){ return a - b}.pop(nums.length);
but I have to be honest. I need to know WHY that works - using .apply(Math,nums). If I just did this:
Math.max(nums);
that would not work.
by using apply, I pass in Math as this - and nums for the array. But I want to know the intricacies of "why" that the first works and the latter doesn't. What magic is happening?
There is something fundamental I am not wrapping my brains around. I have read a bunch about "call and apply", but many times some cool tricks can be had like the one above, and I feel I am missing something deeper here.
As you can read here about Function.prototype.apply, apply Calls a function with a given this value and arguments provided as an array. So the second parameter for the apply function accepts arrays, that's why it works.
While Math.max() itself expects any number of parameters NOT an array of them:
Math.max(66,3,8,213,965,1,453) //will work
Math.max([66,3,8,213,965,1,453]) //will not work
Usually you will be using apply for calling functions with dynamic parameters (e.g. user generated) that you don't know the number of parameters. On the other hand if you have a fixed number of parameters then you can easily provide them to Math.max() function.

Can I be sure that array.slice() will always work the same as array.slice(0)?

I've always used the .slice() method with no arguments to make a copy of a JavaScript Array. This works fine in every browser and JavaScript environment I've tried: it is treated the same as .slice(0).
It's just a matter of style, but to me omitting the start argument entirely makes it more clear that we aren't taking any special sub-slice of the array but want a copy of the whole thing.
However, both MDN and MSDN say that the first argument to array.slice() is required. Only the second argument is optional. Other online sources such as TutorialsPoint and W3Schools say the same thing. (No, I'm not recommending W3Schools! Just pointing out that they agree with MDN and MSDN on this issue.)
Have I just been lucky with this? Could there be a browser or other JavaScript environment where array.slice() doesn't work?
All of these online references are mistaken.
At least if we're talking about standards-complant browsers and runtimes.
The ECMA-262 standard requires any conforming implementation to treat array.slice() identically to array.slice(0).
Here is how we know this.
First we look at Section 15.4.4.10, "Array.prototype.slice (start, end)":
The slice method takes two arguments, start and end, and returns an array containing the elements of the array from element start up to, but not including, element end (or through the end of the array if end is undefined)…
What is this? There's not even any mention of end being optional. Are both start and end required?
Yes, they are. But we need to look elsewhere to understand what this means.
Section 15, "Standard Built-in ECMAScript Objects" says (in the fourth paragraph):
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given fewer arguments than the function is specified to require, the function or constructor shall behave exactly as if it had been given sufficient additional arguments, each such argument being the undefined value.
And this is consistent with how other methods are specified. We know, for example, that the compare function argument to array.sort() is optional, but Section 15.4.4.11 "Array.prototype.sort (comparefn)" doesn't say anything about the comparefn argument being optional. It just describes what to do when comparefn is undefined or not.
So now we know that array.slice() is interpreted as array.slice(undefined,undefined). Continuing, then, with Section 15.4.4.10, we find the relevant step:
5 . Let relativeStart be ToInteger(start).
ToInteger is described in Section 9.4, where the first two steps are relevant:
1 . Let number be the result of calling ToNumber on the input argument.
2 . If number is NaN, return +0.
ToNumber is found in Section 9.3, where the first entry in the table says that when its Argument Type is Undefined, the result is NaN.
So, the missing first argument to array.slice() is treated as undefined and that value is passed into ToNumber, which returns NaN. This causes ToInteger to return 0 (or +0 as they call it here), and that's the value that array.slice() uses.
Therefore, array.slice() is the same as array.slice(0). If an implementation doesn't treat it that way, it doesn't conform to the ECMA-262 standard.
Of course, that's the standard, and then there is the real world.
If we follow this same analysis for the second argument to .slice(), we'll come to the conclusion that all of these should work the same (among other similar variations):
array.slice()
array.slice( 0 )
array.slice( undefined )
array.slice( 0, undefined )
array.slice( undefined, undefined )
However, as #Pumbaa80 points out in a comment, the last two versions do not work in IE8 (nor in IE7)!
But at least the simple array.slice() case I was concerned about does work in these old browsers, and should continue to work in any browser or runtime that follows the standard.

Resources