Related
I know what is a for... in loop (it iterates over the keys), but I have heard about for... of for the first time (it iterates over values).
I am confused about for... of loop.
var arr = [3, 5, 7];
arr.foo = "hello";
for (var i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (var i of arr) {
console.log(i); // logs "3", "5", "7"
// it doesn't log "3", "5", "7", "hello"
}
I understand that for... of iterates over property values. Then why doesn't it log "3", "5", "7", "hello" instead of "3", "5", "7"?
Unlike for... in loop, which iterates over each key ("0", "1", "2", "foo") and also iterates over the foo key, the for... of does not iterate over the value of foo property, i.e., "hello". Why it is like that?
Here I console for... of loop. It should log "3", "5", "7","hello" but it logs "3", "5", "7". Why?
Example Link
for in loops over enumerable property names of an object.
for of (new in ES6) does use an object-specific iterator and loops over the values generated by that.
In your example, the array iterator does yield all the values in the array (ignoring non-index properties).
I found a complete answer at Iterators and Generators (Although it is for TypeScript, this is the same for JavaScript too)
Both for..of and for..in statements iterate over lists; the values
iterated on are different though, for..in returns a list of keys on
the object being iterated, whereas for..of returns a list of values
of the numeric properties of the object being iterated.
Here is an example that demonstrates this distinction:
let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
Another distinction is that for..in operates on any object; it serves
as a way to inspect properties on this object. for..of on the other
hand, is mainly interested in values of iterable objects. Built-in
objects like Map and Set implement Symbol.iterator property allowing
access to stored values.
let pets = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";
for (let pet in pets) {
console.log(pet); // "species"
}
for (let pet of pets) {
console.log(pet); // "Cat", "Dog", "Hamster"
}
Difference for..in and for..of:
Both for..in and for..of are looping constructs which are used to iterate over data structures. The only difference between them is the entities
they iterate over:
for..in iterates over all enumerable property keys of an object
for..of iterates over the values of an iterable object. Examples of iterable objects are arrays, strings, and NodeLists.
Example:
let arr = ['el1', 'el2', 'el3'];
arr.addedProp = 'arrProp';
// elKey are the property keys
for (let elKey in arr) {
console.log(elKey);
}
// elValue are the property values
for (let elValue of arr) {
console.log(elValue)
}
In this example we can observe that the for..in loop iterates over the keys of the object, which is an array object in this example. The keys are 0, 1, 2 (which correspond to the array elements) and addedProp. This is how the arr array object looks in chrome devtools:
You see that our for..in loop does nothing more than simply iterating over these keys.
The for..of loop in our example iterates over the values of a data structure. The values in this specific example are 'el1', 'el2', 'el3'. The values which an iterable data structure will return using for..of is dependent on the type of iterable object. For example an array will return the values of all the array elements whereas a string returns every individual character of the string.
For...in loop
The for...in loop improves upon the weaknesses of the for loop by eliminating the counting logic and exit condition.
Example:
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}
But, you still have to deal with the issue of using an index to access the values of the array, and that stinks; it almost makes it more confusing than before.
Also, the for...in loop can get you into big trouble when you need to add an extra method to an array (or another object). Because for...in loops loop over all enumerable properties, this means if you add any additional properties to the array's prototype, then those properties will also appear in the loop.
Array.prototype.decimalfy = function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
};
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}
Prints:
0
1
2
3
4
5
6
7
8
9
function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
}
This is why for...in loops are discouraged when looping over arrays.
NOTE: The forEach loop is another type of for loop in JavaScript.
However, forEach() is actually an array method, so it can only be used
exclusively with arrays. There is also no way to stop or break a
forEach loop. If you need that type of behavior in your loop, you’ll
have to use a basic for loop.
For...of loop
The for...of loop is used to loop over any type of data that is iterable.
Example:
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
Prints:
0
1
2
3
4
5
6
7
8
9
This makes the for...of loop the most concise version of all the for loops.
But wait, there’s more! The for...of loop also has some additional benefits that fix the weaknesses of the for and for...in loops.
You can stop or break a for...of loop at anytime.
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
if (digit % 2 === 0) {
continue;
}
console.log(digit);
}
Prints:
1
3
5
7
9
And you don’t have to worry about adding new properties to objects. The for...of loop will only loop over the values in the object.
Here is a useful mnemonic for remembering the difference between for...in Loop and for...of Loop.
"index in, object of"
for...in Loop => iterates over the index in the array.
for...of Loop => iterates over the object of objects.
for of is used to iterate over iterables and for in is used to iterate over object properties
Here's a trick to remember:
for of is not for objects (so it's for iterables)
for in is not for iterables (so it's for objects)
Another trick:
for in returns object indices (keys) while for of returns values
//for in, iterates keys in an object and indexes in an array
let obj={a:1, b:2}
for( const key in obj)
console.log(obj[key]); //would print 1 and 2
console.log(key); //would print a and b
let arr = [10, 11, 12, 13];
for (const item in arr)
console.log(item); //would print 0 1 2 3
//for of, iterates values in an array or any iterable
let arr = [10, 11, 12, 13];
for (const item of arr )
console.log(item); //would print 10 11 12 13
Another difference between the two loops, which nobody has mentioned before:
Destructuring for...in is deprecated. Use for...of instead.
Source
So if we want to use destructuring in a loop, for get both index and value of each array element, we should to use the for...of loop with the Array method entries():
for (const [idx, el] of arr.entries()) {
console.log( idx + ': ' + el );
}
The for...in statement iterates over the enumerable properties of an object, in an arbitrary order.
Enumerable properties are those properties whose internal [[Enumerable]] flag is set to true, hence if there is any enumerable property in the prototype chain, the for...in loop will iterate on those as well.
The for...of statement iterates over data that iterable object defines to be iterated over.
Example:
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
for (let i in iterable) {
console.log(i); // logs: 0, 1, 2, "arrCustom", "objCustom"
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs: 0, 1, 2,
}
}
for (let i of iterable) {
console.log(i); // logs: 3, 5, 7
}
Like earlier, you can skip adding hasOwnProperty in for...of loops.
Short answer: for...in loops over keys, while for...of loops over values.
for (let x in ['a', 'b', 'c', 'd'] {
console.log(x);
}
// Output
0
1
2
3
for (let x of ['a', 'b', 'c', 'd'] {
console.log(x);
}
// Output
a
b
c
d
The for-in statement iterates over the enumerable properties of an object, in arbitrary order.
The loop will iterate over all enumerable properties of the object itself and those the object inherits from its constructor's prototype
You can think of it as "for in" basically iterates and list out all the keys.
var str = 'abc';
var arrForOf = [];
var arrForIn = [];
for(value of str){
arrForOf.push(value);
}
for(value in str){
arrForIn.push(value);
}
console.log(arrForOf);
// ["a", "b", "c"]
console.log(arrForIn);
// ["0", "1", "2", "formatUnicorn", "truncate", "splitOnLast", "contains"]
There are some already defined data types which allows us to iterate over them easily e.g Array, Map, String Objects
Normal for in iterates over the iterator and in response provides us with the keys that are in the order of insertion as shown in below example.
const numbers = [1,2,3,4,5];
for(let number in number) {
console.log(number);
}
// result: 0, 1, 2, 3, 4
Now if we try same with for of, then in response it provides us with the values not the keys. e.g
const numbers = [1,2,3,4,5];
for(let numbers of numbers) {
console.log(number);
}
// result: 1, 2, 3, 4, 5
So looking at both of the iterators we can easily differentiate the difference between both of them.
Note:- For of only works with the Symbol.iterator
So if we try to iterate over normal object, then it will give us an error e.g-
const Room = {
area: 1000,
height: 7,
floor: 2
}
for(let prop in Room) {
console.log(prop);
}
// Result area, height, floor
for(let prop of Room) {
console.log(prop);
}
Room is not iterable
Now for iterating over we need to define an ES6 Symbol.iterator e.g
const Room= {
area: 1000, height: 7, floor: 2,
[Symbol.iterator]: function* (){
yield this.area;
yield this.height;
yield this.floors;
}
}
for(let prop of Room) {
console.log(prop);
}
//Result 1000, 7, 2
This is the difference between For in and For of. Hope that it might clear the difference.
The for-in loop
for-in loop is used to traverse through enumerable properties of a collection, in an arbitrary order. A collection is a container type object whose items can be using an index or a key.
var myObject = {a: 1, b: 2, c: 3};
var myArray = [1, 2, 3];
var myString = "123";
console.log( myObject[ 'a' ], myArray[ 1 ], myString[ 2 ] );
for-in loop extracts the enumerable properties (keys) of a collection all at once and iterates over it one at a time. An enumerable property is the property of a collection that can appear in for-in loop.
By default, all properties of an Array and Object appear in for-in loop. However, we can use Object.defineProperty method to manually configure the properties of a collection.
var myObject = {a: 1, b: 2, c: 3};
var myArray = [1, 2, 3];
Object.defineProperty( myObject, 'd', { value: 4, enumerable: false } );
Object.defineProperty( myArray, 3, { value: 4, enumerable: false } );
for( var i in myObject ){ console.log( 'myObject:i =>', i ); }
for( var i in myArray ){ console.log( 'myArray:i =>', i ); }
In the above example, the property d of the myObject and the index 3 of myArray does not appear in for-in loop because they are configured with enumerable: false.
There are few issues with for-in loops. In the case of Arrays, for-in loop will also consider methods added on the array using myArray.someMethod = f syntax, however, myArray.length remains 4.
The for-of loop
It is a misconception that for-of loop iterate over the values of a collection. for-of loop iterates over an Iterable object. An iterable is an object that has the method with the name Symbol.iterator directly on it one on one of its prototypes.
Symbol.iterator method should return an Iterator. An iterator is an object which has a next method. This method when called return value and done properties.
When we iterate an iterable object using for-of loop, the Symbol.iterator the method will be called once get an iterator object. For every iteration of for-of loop, next method of this iterator object will be called until done returned by the next() call returns false. The value received by the for-of loop for every iteration if the value property returned by the next() call.
var myObject = { a: 1, b: 2, c: 3, d: 4 };
// make `myObject` iterable by adding `Symbol.iterator` function directlty on it
myObject[ Symbol.iterator ] = function(){
console.log( `LOG: called 'Symbol.iterator' method` );
var _myObject = this; // `this` points to `myObject`
// return an iterator object
return {
keys: Object.keys( _myObject ),
current: 0,
next: function() {
console.log( `LOG: called 'next' method: index ${ this.current }` );
if( this.current === this.keys.length ){
return { done: true, value: null }; // Here, `value` is ignored by `for-of` loop
} else {
return { done: false, value: _myObject[ this.keys[ this.current++ ] ] };
}
}
};
}
// use `for-of` loop on `myObject` iterable
for( let value of myObject ) {
console.log( 'myObject: value => ', value );
}
The for-of loop is new in ES6 and so are the Iterable and Iterables. The Array constructor type has Symbol.iterator method on its prototype. The Object constructor sadly doesn't have it but Object.keys(), Object.values() and Object.entries() methods return an iterable (you can use console.dir(obj) to check prototype methods). The benefit of the for-of loop is that any object can be made iterable, even your custom Dog and Animal classes.
The easy way to make an object iterable is by implementing ES6 Generator instead of custom iterator implementation.
Unlike for-in, for-of loop can wait for an async task to complete in each iteration. This is achieved using await keyword after for statement documentation.
Another great thing about for-of loop is that it has Unicode support. According to ES6 specifications, strings are stored with UTF-16 encoding. Hence, each character can take either 16-bit or 32-bit. Traditionally, strings were stored with UCS-2 encoding which has supports for characters that can be stored within 16 bits only.
Hence, String.length returns number of 16-bit blocks in a string. Modern characters like an Emoji character takes 32 bits. Hence, this character would return length of 2. for-in loop iterates over 16-bit blocks and returns the wrong index. However, for-of loop iterates over the individual character based on UTF-16 specifications.
var emoji = "😊🤣";
console.log( 'emoji.length', emoji.length );
for( var index in emoji ){ console.log( 'for-in: emoji.character', emoji[index] ); }
for( var character of emoji ){ console.log( 'for-of: emoji.character', character ); }
for...of loop works only with iterable objects. In JavaScript, iterables are objects which can be looped over.
String, Array, TypedArray, Map, and Set are all built-in iterables, because each of their prototype objects implements an ##iterator method. So, for...of loop works on the mentioned object types.
Object in JavaScript is not iterable by default. So, for...of loop does not work on objects.
In simple words, for...of works with strings and arrays but not with objects.
for...in works with those properties whose enumerable flag is set to true.
Enumerable flag for properties created via simple assignment or property initializer are by default true.
Enumerable flag for properties created via Object.defineProperty are by default false.
Here is a more detailed post with examples: https://dev.to/swastikyadav/difference-between-forof-and-forin-loop-in-javascript-j2o
A see a lot of good answers, but I decide to put my 5 cents just to have good example:
For in loop
iterates over all enumerable props
let nodes = document.documentElement.childNodes;
for (var key in nodes) {
console.log( key );
}
For of loop
iterates over all iterable values
let nodes = document.documentElement.childNodes;
for (var node of nodes) {
console.log( node.toString() );
}
When I first started out learning the for in and of loop, I was confused with my output too, but with a couple of research and understanding you can think of the individual loop like the following :
The
for...in loop returns the indexes of the individual property and has no effect of impact on the property's value, it loops and returns information on the property and not the value.
E.g
let profile = {
name : "Naphtali",
age : 24,
favCar : "Mustang",
favDrink : "Baileys"
}
The above code is just creating an object called profile, we'll use it for both our examples, so, don't be confused when you see the profile object on an example, just know it was created.
So now let us use the for...in loop below
for(let myIndex in profile){
console.log(`The index of my object property is ${myIndex}`)
}
// Outputs :
The index of my object property is 0
The index of my object property is 1
The index of my object property is 2
The index of my object property is 3
Now Reason for the output being that we have Four(4) properties in our profile object and indexing as we all know starts from 0...n, so, we get the index of properties 0,1,2,3 since we are working with the for..in loop.
for...of loop* can return either the property, value or both, Let's take a look at how.
In javaScript, we can't loop through objects normally as we would on arrays, so, there are a few elements we can use to access either of our choices from an object.
Object.keys(object-name-goes-here) >>> Returns the keys or properties of an object.
Object.values(object-name-goes-here) >>> Returns the values of an object.
Object.entries(object-name-goes-here) >>> Returns both the keys and values of an object.
Below are examples of their usage, pay attention to Object.entries() :
Step One: Convert the object to get either its key, value, or both.
Step Two: loop through.
// Getting the keys/property
Step One: let myKeys = ***Object.keys(profile)***
Step Two: for(let keys of myKeys){
console.log(`The key of my object property is ${keys}`)
}
// Getting the values of the property
Step One: let myValues = ***Object.values(profile)***
Step Two : for(let values of myValues){
console.log(`The value of my object property is ${values}`)
}
When using Object.entries() have it that you are calling two entries on the object, i.e the keys and values. You can call both by either of the entry. Example Below.
Step One: Convert the object to entries, using ***Object.entries(object-name)***
Step Two: **Destructure** the ***entries object which carries the keys and values***
like so **[keys, values]**, by so doing, you have access to either or both content.
// Getting the keys/property
Step One: let myKeysEntry = ***Object.entries(profile)***
Step Two: for(let [keys, values] of myKeysEntry){
console.log(`The key of my object property is ${keys}`)
}
// Getting the values of the property
Step One: let myValuesEntry = ***Object.entries(profile)***
Step Two : for(let [keys, values] of myValuesEntry){
console.log(`The value of my object property is ${values}`)
}
// Getting both keys and values
Step One: let myBothEntry = ***Object.entries(profile)***
Step Two : for(let [keys, values] of myBothEntry){
console.log(`The keys of my object is ${keys} and its value
is ${values}`)
}
Make comments on unclear parts section(s).
Everybody did explain why this problem occurs, but it's still very easy to forget about it and then scratching your head why you got wrong results. Especially when you're working on big sets of data when the results seem to be fine at first glance.
Using Object.entries you ensure to go trough all properties:
var arr = [3, 5, 7];
arr.foo = "hello";
for ( var [key, val] of Object.entries( arr ) ) {
console.log( val );
}
/* Result:
3
5
7
hello
*/
In simple terms forIN iterates over the KEYS IN the array(index)/object(key),
whereas forOF iterates over the VALUES OF the array(value).
I have some code here and I was wondering if it is the same thing or different. I am pretty sure these are both suppose to be the same but I wasnt sure if I was doing it right.
let zoneComment = updatedMap[action.comment.zone]
? [...updatedMap[action.comment.zone]] : [];
let zoneComment = updatedMap[action.comment.zone]
? Object.assign([], updatedMap[action.comment.zone]) : [];
If these are the same then which should I use or does it matter? I want to use best practice so if it is your OPINION of which is better then please state so.
In your particular case they are not the same.
The reason is that you have an array, not an object.
Doing ... on an array will spread out all the elements in the array (but not the properties)
Doing Object.assign expects an object so it will treat an array as an object and copy all enumerable own properties into it, not just the elements:
const a = [1, 2, 3];
a.test = 'example';
const one = [...a] // [1, 2, 3];
const two = Object.assign([], a); // { '0': 1, '1': 2, '2': 3, 'test': 'example' }
console.log('\none');
for (let prop in one) {
console.log(prop);
}
console.log('\ntwo');
for (let prop in two) {
console.log(prop);
}
However, if you compare the ... operator applied on an object with Object.assign, they are essentially the same:
// same result
const a = { name: 'test' }
console.log({ ...a })
console.log(Object.assign({}, a))
except ... always creates a new object but Object.assign also allows you to mutate an existing object.
// same result
const a = { name: 'test' }
const b = { ...a, name: 'change' };
console.log(a.name); // test
Object.assign(a, { name: 'change'})
console.log(a.name); // change
Keep in mind that Object.assign is already a part of the language whereas object spread is still only a proposal and would require a preprocessing step (transpilation) with a tool like babel.
To make it short, always use ... spread construction and never Object.assign on arrays.
Object.assign is intended for objects. Although arrays are objects, too, it will cause a certain effect on them which is useful virtually never.
Object.assign(obj1, obj2) gets values from all enumerable keys from obj2 and assigns them to obj1. Arrays are objects, and array indexes are object keys, in fact.
[...[1, 2, 3], ...[4, 5]] results in [1, 2, 3, 4, 5] array.
Object.assign([1, 2, 3], [4, 5]) results in [4, 5, 3] array, because values on 0 and 1 indexes in first array are overwritten with values from second array.
In the case when first array is empty, Object.assign([], arr) and [...arr] results are similar. However, the proper ES5 alternative to [...arr] is [].concat(arr) and not Object.assign([], arr).
Your question really bubbles down to:
Are [...arr] and Object.assign([], arr) providing the same result when arr is an array?
The answer is: usually, yes, but:
if arr is a sparse array that has no value for its last slot, then the length property of the result will not be the same in both cases: the spread syntax will maintain the same value for the length property, but Object.assign will produce an array with a length that corresponds to the index of the last used slot, plus one.
if arr is a sparse array (like what you get with Array(10)) then the spread syntax will create an array with undefined values at those indexes, so it will not be a sparse array. Object.assign on the other hand, will really keep those slots empty (non-existing).
if arr has custom enumerable properties, they will be copied by Object.assign, but not by the spread syntax.
Here is a demo of the first two of those differences:
var arr = ["abc"]
arr[2] = "def"; // leave slot 1 empty
arr.length = 4; // empty slot at index 3
var a = [...arr];
var b = Object.assign([], arr);
console.log(a.length, a instanceof Array); // 4, true
console.log(b.length, b instanceof Array); // 3, true
console.log('1' in arr); // false
console.log('1' in a); // true (undefined)
console.log('1' in b); // false
If however arr is a standard array (with no extra properties) and has all its slots filled, then both ways produce the same result:
Both return an array. [...arr] does this by definition, and Object.assign does this because its first argument is an array, and it is that object that it will return: mutated, but it's proto will not change. Although length is not an enumerable property, and Object.assign will not copy it, the behaviour of the first-argument array is that it will adapt its length attribute as the other properties are assigned to it.
Both take shallow copies.
Conclusion
If your array has custom properties you want to have copied, and it has no empty slots at the end: use Object.assign.
If your array has no custom properties (or you don't care about them) and does not have empty slots: use the spread syntax.
If your array has custom properties you want to have copied, and empty slots you want to maintain: neither method will do both of this. But with Object.assign it is easier to accomplish:
a = Object.assign([], arr, { length: arr.length });
Currently I have an array using an increasing index:
var idx = 1;
var a = [];
a[idx++] = "apple";
a[idx++] = "orange";
...
console.log(a[2]);
And only accessing it by [], not using array specific functions, like length, indexOf, ...
Apparently following is also working in this case:
var a = {};
So, which one should I prefer in such case? For example any performance difference between them?
[ ] denotes an array. Arrays only hold values:
var a1 = [1, 2, 3, 4]
As #Qantas pointed out, array can hold more than just values. An array can even contain another array and/or object:
var a2 = [1, 2, ["apple", "orange"], {one: "grape", two: "banana"}];
{ } denotes an object. Objects have key-value pairs like
var a3 = {one: 1, two: 2}
In your case, it's really a matter of how you would like to be able to access the data. If you are only interested in knowing "apple", "pear", etc. Go ahead and use an array. You can access it via it's index
a1[0]; // outputs 1
a1[1]; // outputs 2
or you can iterate over it with a loop. If you use the curly braces, (given the example I gave) you could access it with
a3.one; // outputs 1
a3["two"]; // outputs 2
It's really up to you on how it would best fit your needs in this case. For a more extensive discussion see this article.
The difference is using square brackets will create an Array object while using curly brackets creates a plain object. For example:
a = [];
a[1] = 'a';
b = {};
b[1] = 'b';
a.length; // returns 2
b.length; // is undefined
a.push('z'); // add 'z' to the end of a
b.push('z'); // generates an error - undefined is not a function
// because plain objects don't have a push method
Read the MDN documentation on Array objects to know more about arrays: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
I'm diving into the Mars Rover Kata. I'm running into an interesting problem. My jasmine tests are failing due to my array compare method in the prototype. I'm using the array compare method to detect obstacles at a specified coordinate or grid point.
As an example my first test produces this error: Expected [ 0, 1, 'N', undefined ] to equal [ 0, 1, 'N' ].
When I console.log my array I get: [0, 1, "N", compare: function]. This explains why it's not equal to [0, 1, 'N'].
The length of my array is 3 and the proto has the compare method. What can I do about this issue?
Here is the branch/code. Here are the tests.
Update:
It turns out that one of my conditionals was returning undefined which was causing my error of undefined being pushed into my array, but without trying #GameAlchemist solution I wouldn't have learned about defineProperty or figured out the problem.
In addition, from what I've read it is good practice when adding properties to built-in prototypes to use Object.defineProperty() and similar methods to make them non-enumerable. That will give you some protection against breaking for-in loops in legacy code.
You can use Object.defineProperty to hide a prototype property/method by setting it as non-enumerable.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
So if you're the one defining compare, do it like :
Object.defineProperty(Array.prototype, 'compare', { value : compareFunction } );
Since configurable, enumerable, and writable are false by default, you'll have a readonly, non-configurable and non-enumerable property : it won't stand in the way of your comparisons.
If you're not the one defining compare, you have to hope it is still configurable (look with Object. getOwnPropertyDescriptor or just test the following code) :
Object.defineProperty(Array.prototype, 'compare', { value : Array.prototype.compare } );
In the same way, you can do a loop on all Array prototype to check if every of its own property is enumerable, and set it to non-enumerable if it is.
http://jsbin.com/zunahebo/2/edit?js,console
Object.defineProperty(Array.prototype, 'compare', { value : compareArray } );
function compareArray(other) {
if (!other || other.length != this.length) return false;
for (var i=0; i<this.length; i++) if (this[i] !== other[i]) return false;
return true;
}
var a1 = [1, 2, 3, 4];
var a2 = [1, 2, 3, 4];
var a3 = [1, 2, 5, 6];
var a4 = [1, 2];
console.log(' a1 == a2 : ' + a1.compare(a2));
console.log(' a1 == a3 : ' + a1.compare(a3));
console.log(' a1 == a4 : ' + a1.compare(a4));
perhaps:
for(var i = 0, l=this.length; i < l; i++) {
if (this.hasOwnProperty(i)){
...
}
}
I've run into the following code in the es-discuss mailing list:
Array.apply(null, { length: 5 }).map(Number.call, Number);
This produces
[0, 1, 2, 3, 4]
Why is this the result of the code? What's happening here?
Understanding this "hack" requires understanding several things:
Why we don't just do Array(5).map(...)
How Function.prototype.apply handles arguments
How Array handles multiple arguments
How the Number function handles arguments
What Function.prototype.call does
They're rather advanced topics in javascript, so this will be more-than-rather long. We'll start from the top. Buckle up!
1. Why not just Array(5).map?
What's an array, really? A regular object, containing integer keys, which map to values. It has other special features, for instance the magical length variable, but at it's core, it's a regular key => value map, just like any other object. Let's play with arrays a little, shall we?
var arr = ['a', 'b', 'c'];
arr.hasOwnProperty(0); //true
arr[0]; //'a'
Object.keys(arr); //['0', '1', '2']
arr.length; //3, implies arr[3] === undefined
//we expand the array by 1 item
arr.length = 4;
arr[3]; //undefined
arr.hasOwnProperty(3); //false
Object.keys(arr); //['0', '1', '2']
We get to the inherent difference between the number of items in the array, arr.length, and the number of key=>value mappings the array has, which can be different than arr.length.
Expanding the array via arr.length does not create any new key=>value mappings, so it's not that the array has undefined values, it does not have these keys. And what happens when you try to access a non-existent property? You get undefined.
Now we can lift our heads a little, and see why functions like arr.map don't walk over these properties. If arr[3] was merely undefined, and the key existed, all these array functions would just go over it like any other value:
//just to remind you
arr; //['a', 'b', 'c', undefined];
arr.length; //4
arr[4] = 'e';
arr; //['a', 'b', 'c', undefined, 'e'];
arr.length; //5
Object.keys(arr); //['0', '1', '2', '4']
arr.map(function (item) { return item.toUpperCase() });
//["A", "B", "C", undefined, "E"]
I intentionally used a method call to further prove the point that the key itself was never there: Calling undefined.toUpperCase would have raised an error, but it didn't. To prove that:
arr[5] = undefined;
arr; //["a", "b", "c", undefined, "e", undefined]
arr.hasOwnProperty(5); //true
arr.map(function (item) { return item.toUpperCase() });
//TypeError: Cannot call method 'toUpperCase' of undefined
And now we get to my point: How Array(N) does things. Section 15.4.2.2 describes the process. There's a bunch of mumbo jumbo we don't care about, but if you manage to read between the lines (or you can just trust me on this one, but don't), it basically boils down to this:
function Array(len) {
var ret = [];
ret.length = len;
return ret;
}
(operates under the assumption (which is checked in the actual spec) that len is a valid uint32, and not just any number of value)
So now you can see why doing Array(5).map(...) wouldn't work - we don't define len items on the array, we don't create the key => value mappings, we simply alter the length property.
Now that we have that out of the way, let's look at the second magical thing:
2. How Function.prototype.apply works
What apply does is basically take an array, and unroll it as a function call's arguments. That means that the following are pretty much the same:
function foo (a, b, c) {
return a + b + c;
}
foo(0, 1, 2); //3
foo.apply(null, [0, 1, 2]); //3
Now, we can ease the process of seeing how apply works by simply logging the arguments special variable:
function log () {
console.log(arguments);
}
log.apply(null, ['mary', 'had', 'a', 'little', 'lamb']);
//["mary", "had", "a", "little", "lamb"]
//arguments is a pseudo-array itself, so we can use it as well
(function () {
log.apply(null, arguments);
})('mary', 'had', 'a', 'little', 'lamb');
//["mary", "had", "a", "little", "lamb"]
//a NodeList, like the one returned from DOM methods, is also a pseudo-array
log.apply(null, document.getElementsByTagName('script'));
//[script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script]
//carefully look at the following two
log.apply(null, Array(5));
//[undefined, undefined, undefined, undefined, undefined]
//note that the above are not undefined keys - but the value undefined itself!
log.apply(null, {length : 5});
//[undefined, undefined, undefined, undefined, undefined]
It's easy to prove my claim in the second-to-last example:
function ahaExclamationMark () {
console.log(arguments.length);
console.log(arguments.hasOwnProperty(0));
}
ahaExclamationMark.apply(null, Array(2)); //2, true
(yes, pun intended). The key => value mapping may not have existed in the array we passed over to apply, but it certainly exists in the arguments variable. It's the same reason the last example works: The keys do not exist on the object we pass, but they do exist in arguments.
Why is that? Let's look at Section 15.3.4.3, where Function.prototype.apply is defined. Mostly things we don't care about, but here's the interesting portion:
Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
Which basically means: argArray.length. The spec then proceeds to do a simple for loop over length items, making a list of corresponding values (list is some internal voodoo, but it's basically an array). In terms of very, very loose code:
Function.prototype.apply = function (thisArg, argArray) {
var len = argArray.length,
argList = [];
for (var i = 0; i < len; i += 1) {
argList[i] = argArray[i];
}
//yeah...
superMagicalFunctionInvocation(this, thisArg, argList);
};
So all we need to mimic an argArray in this case is an object with a length property. And now we can see why the values are undefined, but the keys aren't, on arguments: We create the key=>value mappings.
Phew, so this might not have been shorter than the previous part. But there'll be cake when we finish, so be patient! However, after the following section (which'll be short, I promise) we can begin dissecting the expression. In case you forgot, the question was how does the following work:
Array.apply(null, { length: 5 }).map(Number.call, Number);
3. How Array handles multiple arguments
So! We saw what happens when you pass a length argument to Array, but in the expression, we pass several things as arguments (an array of 5 undefined, to be exact). Section 15.4.2.1 tells us what to do. The last paragraph is all that matters to us, and it's worded really oddly, but it kind of boils down to:
function Array () {
var ret = [];
ret.length = arguments.length;
for (var i = 0; i < arguments.length; i += 1) {
ret[i] = arguments[i];
}
return ret;
}
Array(0, 1, 2); //[0, 1, 2]
Array.apply(null, [0, 1, 2]); //[0, 1, 2]
Array.apply(null, Array(2)); //[undefined, undefined]
Array.apply(null, {length:2}); //[undefined, undefined]
Tada! We get an array of several undefined values, and we return an array of these undefined values.
The first part of the expression
Finally, we can decipher the following:
Array.apply(null, { length: 5 })
We saw that it returns an array containing 5 undefined values, with keys all in existence.
Now, to the second part of the expression:
[undefined, undefined, undefined, undefined, undefined].map(Number.call, Number)
This will be the easier, non-convoluted part, as it doesn't so much rely on obscure hacks.
4. How Number treats input
Doing Number(something) (section 15.7.1) converts something to a number, and that is all. How it does that is a bit convoluted, especially in the cases of strings, but the operation is defined in section 9.3 in case you're interested.
5. Games of Function.prototype.call
call is apply's brother, defined in section 15.3.4.4. Instead of taking an array of arguments, it just takes the arguments it received, and passes them forward.
Things get interesting when you chain more than one call together, crank the weird up to 11:
function log () {
console.log(this, arguments);
}
log.call.call(log, {a:4}, {a:5});
//{a:4}, [{a:5}]
//^---^ ^-----^
// this arguments
This is quite wtf worthy until you grasp what's going on. log.call is just a function, equivalent to any other function's call method, and as such, has a call method on itself as well:
log.call === log.call.call; //true
log.call === Function.call; //true
And what does call do? It accepts a thisArg and a bunch of arguments, and calls its parent function. We can define it via apply (again, very loose code, won't work):
Function.prototype.call = function (thisArg) {
var args = arguments.slice(1); //I wish that'd work
return this.apply(thisArg, args);
};
Let's track how this goes down:
log.call.call(log, {a:4}, {a:5});
this = log.call
thisArg = log
args = [{a:4}, {a:5}]
log.call.apply(log, [{a:4}, {a:5}])
log.call({a:4}, {a:5})
this = log
thisArg = {a:4}
args = [{a:5}]
log.apply({a:4}, [{a:5}])
The later part, or the .map of it all
It's not over yet. Let's see what happens when you supply a function to most array methods:
function log () {
console.log(this, arguments);
}
var arr = ['a', 'b', 'c'];
arr.forEach(log);
//window, ['a', 0, ['a', 'b', 'c']]
//window, ['b', 1, ['a', 'b', 'c']]
//window, ['c', 2, ['a', 'b', 'c']]
//^----^ ^-----------------------^
// this arguments
If we don't provide a this argument ourselves, it defaults to window. Take note of the order in which the arguments are provided to our callback, and let's weird it up all the way to 11 again:
arr.forEach(log.call, log);
//'a', [0, ['a', 'b', 'c']]
//'b', [1, ['a', 'b', 'c']]
//'b', [2, ['a', 'b', 'c']]
// ^ ^
Whoa whoa whoa...let's back up a bit. What's going on here? We can see in section 15.4.4.18, where forEach is defined, the following pretty much happens:
var callback = log.call,
thisArg = log;
for (var i = 0; i < arr.length; i += 1) {
callback.call(thisArg, arr[i], i, arr);
}
So, we get this:
log.call.call(log, arr[i], i, arr);
//After one `.call`, it cascades to:
log.call(arr[i], i, arr);
//Further cascading to:
log(i, arr);
Now we can see how .map(Number.call, Number) works:
Number.call.call(Number, arr[i], i, arr);
Number.call(arr[i], i, arr);
Number(i, arr);
Which returns the transformation of i, the current index, to a number.
In conclusion,
The expression
Array.apply(null, { length: 5 }).map(Number.call, Number);
Works in two parts:
var arr = Array.apply(null, { length: 5 }); //1
arr.map(Number.call, Number); //2
The first part creates an array of 5 undefined items. The second goes over that array and takes its indices, resulting in an array of element indices:
[0, 1, 2, 3, 4]
Disclaimer: This is a very formal description of the above code - this is how I know how to explain it. For a simpler answer - check Zirak's great answer above. This is a more in depth specification in your face and less "aha".
Several things are happening here. Let's break it up a bit.
var arr = Array.apply(null, { length: 5 }); // Create an array of 5 `undefined` values
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
In the first line, the array constructor is called as a function with Function.prototype.apply.
The this value is null which does not matter for the Array constructor (this is the same this as in the context according to 15.3.4.3.2.a.
Then new Array is called being passed an object with a length property - that causes that object to be an array like for all it matters to .apply because of the following clause in .apply:
Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
As such, .apply is passing arguments from 0 to .length , since calling [[Get]] on { length: 5 } with the values 0 to 4 yields undefined the array constructor is called with five arguments whose value is undefined (getting an undeclared property of an object).
The array constructor is called with 0, 2 or more arguments.
The length property of the newly constructed array is set to the number of arguments according to the specification and the values to the same values.
Thus var arr = Array.apply(null, { length: 5 }); creates a list of five undefined values.
Note: Notice the difference here between Array.apply(0,{length: 5}) and Array(5), the first creating five times the primitive value type undefined and the latter creating an empty array of length 5. Specifically, because of .map's behavior (8.b) and specifically [[HasProperty] .
So the code above in a compliant specification is the same as:
var arr = [undefined, undefined, undefined, undefined, undefined];
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
Now off to the second part.
Array.prototype.map calls the callback function (in this case Number.call) on each element of the array and uses the specified this value (in this case setting the this value to `Number).
The second parameter of the callback in map (in this case Number.call) is the index, and the first is the this value.
This means that Number is called with this as undefined (the array value) and the index as the parameter. So it's basically the same as mapping each undefined to its array index (since calling Number performs type conversion, in this case from number to number not changing the index).
Thus, the code above takes the five undefined values and maps each to its index in the array.
Which is why we get the result to our code.
As you said, the first part:
var arr = Array.apply(null, { length: 5 });
creates an array of 5 undefined values.
The second part is calling the map function of the array which takes 2 arguments and returns a new array of the same size.
The first argument which map takes is actually a function to apply on each element in the array, it is expected to be a function which takes 3 arguments and returns a value.
For example:
function foo(a,b,c){
...
return ...
}
if we pass the function foo as the first argument it will be called for each element with
a as the value of the current iterated element
b as the index of the current iterated element
c as the whole original array
The second argument which map takes is being passed to the function which you pass as the first argument. But it would not be a, b, nor c in case of foo, it would be this.
Two examples:
function bar(a,b,c){
return this
}
var arr2 = [3,4,5]
var newArr2 = arr2.map(bar, 9);
//newArr2 is equal to [9,9,9]
function baz(a,b,c){
return b
}
var newArr3 = arr2.map(baz,9);
//newArr3 is equal to [0,1,2]
and another one just to make it clearer:
function qux(a,b,c){
return a
}
var newArr4 = arr2.map(qux,9);
//newArr4 is equal to [3,4,5]
So what about Number.call ?
Number.call is a function that takes 2 arguments, and tries to parse the second argument to a number (I'm not sure what it does with the first argument).
Since the second argument that map is passing is the index, the value that will be placed in the new array at that index is equal to the index. Just like the function baz in the example above. Number.call will try to parse the index - it will naturally return the same value.
The second argument you passed to the map function in your code doesn't actually have an effect on the result. Correct me if I'm wrong, please.
An array is simply an object comprising the 'length' field and some methods (e.g. push). So arr in var arr = { length: 5} is basically the same as an array where the fields 0..4 have the default value which is undefined (i.e. arr[0] === undefined yields true).
As for the second part, map, as the name implies, maps from one array to a new one. It does so by traversing through the original array and invoking the mapping-function on each item.
All that's left is to convince you that the result of mapping-function is the index. The trick is to use the method named 'call'(*) which invokes a function with the small exception that the first param is set to be the 'this' context, and the second becomes the first param (and so on). Coincidentally, when the mapping-function is invoked, the second param is the index.
Last but not least, the method which is invoked is the Number "Class", and as we know in JS, a "Class" is simply a function, and this one (Number) expects the first param to be the value.
(*) found in Function's prototype (and Number is a function).
MASHAL