Calling reduce on an empty array throws TypeError which is perfectly understandable and helps catching bugs. But when I call it on an array with a single item inside, the behavior confuses me:
var arr = ["a"];
arr.reduce(function(a,b){
return [a,b]
}); //returns "a"
I know that reduce is not meant to be used on such an array, but I find that returning just the element without invoking the callback or throwing an error is at least strange.
Furthermore, the MDN documentation states that the callback is a "Function to execute on each value in the array, taking four arguments:".
Can someone explain the reasoning behind this behaviour?
The callback is supposed to be a "binary function" (i.e. one that takes two arguments to operate on, plus the additional two arguments that hold the currentIndex and the original array).
If only one element is supplied, you would be passing an undefined value to the callback for either currentValue or previousValue, potentially causing runtime errors.
The design therefore assumes that given only one value (whether that be an empty array, and an initialValue, or an array with one value and no initialValue) it's better not to invoke the callback at all.
Related
I have been refreshing my JavaScript knowledge of call() and map() usage on NodeList.
It was fairly easy to google out, what call() should be doing and there are resources with examples of how it works with map().
However, as I noticed on MDN, the map() function can also take a second argument, which should be setting the this keyword for map() - or at least that is what I think it should be doing.
I have tried to check it myself with simple arrays:
var numbers = [1, 2, 3];
var letters = ['a', 'b', 'c'];
..and with a simple function, that is about to be given as a parameter to map():
var effector = function (x) {
console.log(x);
}
Now, what I do not understand, is why these two function calls have different results:
numbers.map(effector, letters);
numbers.map.call(letters, effector);
I expect them to both output letters to the console, as both this keywords should be referencing to them (respectively to their objects).
I did some further research, and tried with this modified effector function:
var effector = function () {
console.log(this);
}
..again on:
numbers.map(effector, letters);
numbers.map.call(letters, effector);
Assuming, that we are in "use strict", the first call logs letters and the second logs undefined.
But again, I would expect both these calls to produce the same output.
What am I missing?
EDIT:
I was reading, how .map can be polyfilled, if you check it on MDN, there you see, how the second parameter of .map is used in callback.call().
I suppose, that in the end, even that callback.call() should have the same this as was in .map.
MDN - map
There are two bindings of the this reference at play here:
this for the execution context of the map function
this for the execution context of the callback function
They don't relate to each other.
The second argument of map dictates what this will be for the callback. If it is not provided, the default is undefined (not the array).
The first argument of map.call dictates what this will be for map -- and by consequence which array will be iterated.
This is also reflected in the polyfill provided on mdn: it is perfectly in line with these specifications: O gets the value of this for the map function, and T gets the value of this for the callback. They are generally different.
map versus forEach
Unrelated to your question, but worth mentioning: don't use map when you are not actually mapping anything. map is intended for creating a new array, one in which every value has been mapped by calling the callback function on the original value at that same index.
When however you just need to iterate the array values, without any intent to perform such mapping, then use the forEach method, or a for...of loop. Both these work on NodeList out of the box, without the need to .call.
The difference between numbers.map(effector, letters) and numbers.map.call(letters, effector) lies in which function the this is set to letters.
In the first, as MDN explains, the second argument is the value to use as this inside the callback - ie the argument supplied to map. That is, in numbers.map(effector, letters), the this inside effector will be letters. Since effector (in the first version) doesn't care what this is, that argument essentially has no effect, and the contents of numbers are logged.
In numbers.map.call(letters, effector), on the other hand, it is numbers.map that has its this set to letters. Which effectively means the map method, although called on numbers, is "hijacked" into logging the entries of letters instead.
The second example works similarly - the crucial thing is which function the this is set in: effector in one case, numbers.map in the other.
I'm trying to understand how to read the code below (taken from MDN's article on Array.prototype.slice) to understand what happens when it runs.
function list() {
return Array.prototype.slice.call(arguments);
}
My understanding is that the return statement gets a reference to the Array.protoype.slice method. This leads to my first question, "if this is a reference to the slice method, why doesn't it need to be invoked, e.g. Array.prototype.slice().call(arguments)?"
Assuming that this is a call to the slice method, and since there is no argument being immediately passed into it, my second question is "is JS 'seeing' the call method chained to slice and then trying to resolve a value to pass to slice from the call(arguments) method?"
If this is the case, is this method chaining and is this how JS performs chaining operations: from left to right and when there is no argument explicity passed to a method, it tries to resolve a value from a subsequent method to return implicitily to the "empty" callee on the left?
Thanks.
Why doesn't it need to be invoked? — because, as you say, it's a reference, and that's all that's desired. The code wants a reference to the function, not a result returned from calling the function.
Is JS 'seeing' the call method chained to slice and then trying to resolve a value to pass to slice from the call(arguments) method? — well I'm not sure what that means. The reference to the .slice() function is used to get access to the .call() method (inherited from the Function prototype). That function (slice.call) is invoked and passed the arguments object as its first parameter. The result is that slice will be invoked as if it were called like arguments.slice() — which is not possible directly, as the .slice() function isn't available that way.
Overall, what the code is doing is "borrowing" the .slice() method from the Array prototype and using it as if the arguments object were an array.
Sometimes you'll see that written like this:
return [].slice.call(arguments);
It's a little shorter, and it does the same thing (at the expense of the creation of an otherwise unused array instance).
call in javascript is a way of invoking a method within the function, which hard binds the context of this within a function to the parameter passed to it..For more detail regarding callgo through the link below
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
If you want to know more about this and why call is being used, I highly recommend you to go through this github repo:
https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch1.md
Secondly, by default every regular function expression in JS has an arguments object, which is an iterable which is nothing but the list of parameters passed to that function.
function foo()
{
console.log(arguments); //1,2
}
foo(1,2)
More about arguments
https://www.google.co.in/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=arguments%20in%20javascript
And if you want to learn JS properly, blindly go through this repo:
https://github.com/getify/You-Dont-Know-JS
Have a look at the docs for Function.prototype.call():
The call() method calls a function with a given this value and arguments provided individually.
Since .call is a part of Function.prototype, every function has it as a property, including Array.prototype.slice (or [].slice).
According to the docs for Array.prototype.slice:
If begin is undefined, slice begins from index 0. If end is omitted, slice extracts through the end of the sequence (arr.length).
It's getting a reference to the Array.prototype.slice property (function) and running its .call function. The 1st parameter to call is the context (or this value) and any other parameters are passed to the chained method. .slice() doesn't require any parameters and without any it just returns the elements as an array.
So, what's happening is that it's taking arguments - which is not actually an array, just an "array-like" object (with numeric properties and a .length property) - and running .slice() on it like it was an array. This makes it r"convert" arguments into an array.
I have module that returns an object to a callback with the structure of the following pseudocode:
module.exports=function(){
global.foo=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
global.bar=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
// This one is where i ran into trouble
global.foobar=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
// right here
if(callbacks.length===[].length){
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
}
var conf={'pseudo':'object'};
return global['foo'](conf,'foo','bar','foobar');
}
Everything works fine until foobar, and what happens is that when I get down to checking if there are anymore callbacks - because if their are, I want to call them - callbacks.length is 1 at this point. This didn't make sense to me, and I discovered that at that point callbacks actually equaled [[]]. I don't know why this is getting returned, so I guess I have two questions. Can anyone see why callbacks is equal to [[]]?
What I discovered along the way is that when using Strings called as functions in the global namespace - as in var bar='foo'; global[bar]() calls global.foo() - multiple brackets are ignored. So for example, global[[[[[[['foo']]]]]]] === global['foo']. Also weirdly enough (at least to me), the following:
// With
global.bar='foo';
global.foo=function(){return true;}
//the following
global[global[bar]]()
// throws a TypeError: undefined is not a function
None of that makes sense to me.
I recognize that this question ends up asking three questions, and is a bit disorganized, but frankly I'm a bit confused, and I'm not really sure how I want to ask what I want to know, so I just said everything.
My main questions is regarding the multiple brackets, and why that works.
Thanks
My main questions is regarding the multiple brackets, and why that
works.
The multiple brackets works only because it's trying to do a string conversion to get a property name. So, no matter how many nested arrays you have, it ends up calling .toString() on each array and since the inner array only has a single item that has a string in it, multiple .toString() calls just end up resolving to the inner string.
Here's a demo of the same concept in a browser:
window.foo = "hi"
document.write(window[[[[["foo"]]]]]);
For some further explanation:
["foo"].toString() === "foo";
So, then:
[["foo"].toString()].toString() === "foo"
But, if the outer .toString() is there, it is already driving things to a string so you can remove the inner .toString() and thus you get:
[["foo"]].toString() === "foo"
And, you can then nest it as many levels as you want as long as something on the outer level is driving it to a string.
And, since property names are always strings, when you do:
global[[[[[[['foo']]]]]]]
You're ultimately asking for a property name that can be looked up on the global object and since the property name is a string, that calls .toString() on the outer array. When the outer array goes to convert it's only item to a string, it asks that one item to convert itself to a string so this:
global[[[[[[['foo']]]]]]]
turns into this:
global[[[[[[['foo'].toString()].toString()].toString()].toString()].toString()].toString()]
Which hopefully explains why you end up with nothing more than this in the end:
global["foo"]
FYI, if you look at the ES5/ES6 spec for Array.prototype.toString(), it ends up calling array.join() which for a single element array just ends up doing a .toString() on that single element which is how it causes all the nested arrays to just call .toString() on themselves. The outer array calls .toString() on the first nested array which called .toString() on it's single item which is the next nested array and so on until it finally gets to the inner string which is returned back from all the .toString() calls. And, it matters not how many arrays deep it is nested.
Spec reference for Array.prototype.toString(): http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.tostring
Spec reference for Array.prototype.join() which is called by Array.prototype.toString(): http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.join
In your other scenario here:
// With
global.bar='foo';
global.foo=function(){return true;}
//the following
global[global[bar]]()
// throws a TypeError: undefined is not a function
None of that makes sense to me.
Here's what is going on in global[global[bar]]() one piece at a time:
bar resolves to the string 'foo'.
So, global[bar] resolves to global['foo'] which is your function.
But, then you try to do another global reference on it like this global[global[bar]], then you are essentially trying to do:
global[yourfunction]
or
global[global.foo]
That will try to convert yourfunction to a string and look up that property on the global object. That will be undefined. So, you will then try to do undefined() which is a TypeError because undefined is not a function.
What will work in this case is just:
global[bar]()
numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(i){
return (i > 2);
});
I don't understand how this works. if I omit the i as a function argument it breaks the function but the i isn't tied to anything so why does it need to be there?
.filter (Array.prototype.filter) calls the supplied function with 3 arguments:
function(element, index, array) {
...
element is the particular array element for the call.
index is the current index of the element
array is the array being filtered.
You can use any or all of the arguments.
In your case, i refers to the element and is used in the body of your function:
function(i){
return (i > 2);
}
In other words, "filter elements where element is greater than 2".
i is a reference to the current object in the set when inside that closure. It could be named anything as it is just a variable, but then would have to have the same name inside the closure. Instead of using function(){} you could use a callback which is how filter was designed.
The reference is done implicitly by the definition of .filter, you can read more here: http://msdn.microsoft.com/en-us/library/ff679973(v=vs.94).aspx
The i is actually very important. It tells gives the filter function information about the elements it's acting on. In fact it's used right here (i > 2).
This keeps elements whose value is greater than 2.
That i is the formal parameter for the function you are supplying to .filter(). If you do not insert it, the function will not have any way¹ to refer to the argument it's being passed (the i inside the function body will then refer to some other entity that might not even be defined -- window.i would be typical).
¹ that is technically a lie, but consider it true for the purposes of this discussion
An old thread indeed, but just filling in what remains unsaid.
The parentheses are there for you the programmer to insert whatever variable name makes sense for your specific program.
If you choose 'i', most other (beginner) programmers might think 'Oh, i means index'. Which would be wrong.
If you use one argument instead of three, I'd choose 'el' to represent the element, or if your array contains flavors of soda, I'd choose 'flavor'.
That's ES5 notation and maybe if you see it in ES6 notation you would understand why the "i" is a must:
numbers.filter(i => i > 2);
A variable must always be used to refer to the item of the array that you process in each iteration (in this case "i"). It has to be passed as argument to the entry point of the function (in ES6 that goes before the arrow).
I'm currently reading through this jquery masking plugin to try and understand how it works, and in numerous places the author calls the slice() function passing no arguments to it. For instance here the _buffer variable is slice()d, and _buffer.slice() and _buffer seem to hold the same values.
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
//functionality fn
function unmaskedvalue($input, skipDatepickerCheck) {
var input = $input[0];
if (tests && (skipDatepickerCheck === true || !$input.hasClass('hasDatepicker'))) {
var buffer = _buffer.slice();
checkVal(input, buffer);
return $.map(buffer, function(element, index) {
return isMask(index) && element != getBufferElement(_buffer.slice(), index) ? element : null; }).join('');
}
else {
return input._valueGet();
}
}
The .slice() method makes a (shallow) copy of an array, and takes parameters to indicate which subset of the source array to copy. Calling it with no arguments just copies the entire array. That is:
_buffer.slice();
// is equivalent to
_buffer.slice(0);
// also equivalent to
_buffer.slice(0, _buffer.length);
EDIT: Isn't the start index mandatory? Yes. And no. Sort of. JavaScript references (like MDN) usually say that .slice() requires at least one argument, the start index. Calling .slice() with no arguments is like saying .slice(undefined). In the ECMAScript Language Spec, step 5 in the .slice() algorithm says "Let relativeStart be ToInteger(start)". If you look at the algorithm for the abstract operation ToInteger(), which in turn uses ToNumber(), you'll see that it ends up converting undefined to 0.
Still, in my own code I would always say .slice(0), not .slice() - to me it seems neater.
array.slice() = array shallow copy and is a shorter form of array.slice()
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
Yes there may be a reason in the following cases (for which we do not have a clue, on whether they apply, in the provided code):
checkVal() or getBufferElement() modify the content of the arrays passed to them (as second and first argument respectively). In this case the code author wants to prevent the global variable _buffer's content from being modified when calling unmaskedvalue().
The function passed to $.map runs asynchronously. In this case the code author wants to make sure that the passed callback will access the array content as it was during unmaskedvalue() execution (e.g. Another event handler could modify _buffer content after unmaskedvalue() execution and before $.map's callback execution).
If none of the above is the case then, yes, the code would equally work without using .slice(). In this case maybe the code author wants to play safe and avoid bugs from future code changes that would result in unforeseen _buffer content modifications.
Note:
When saying: "prevent the global variable _buffer's content from being modified" it means to achieve the following:
_buffer[0].someProp = "new value" would reflect in the copied array.
_buffer[0] = "new value" would not reflect in the copied array.
(For preventing changes also in the first bullet above, array deep clone can be used, but this is out of the discussed context)
Note 2:
In ES6
var buffer = _buffer.slice();
can also be written as
var buffer = [..._buffer];