In JavaScript, some methods make a copy of the object that invoked it while others do not.
For example:
var numbers = [1, 2, 3, 4, 5];
numbers.map(function(x) { return x + 1 });
console.log(numbers); // [1, 2, 3, 4, 5];
It makes a copy of "numbers" that you have to set to another variable.
Whereas:
var numbers = [1, 2, 3, 4, 5];
numbers.reverse();
console.log(numbers); // [5, 4, 3, 2, 1];
It changes the "numbers" directly. Could anyone please explain why?
This is due to the difference in time of incorporation of methods into the JavaScript.
Method reverse was there from the first version of ECMAScript.
The map was added relatively recently in the 5th version.
There is a trend to be more functional nowadays among languages. One of the main principles in functional languages is immutability of data. Therefore, these new methods of the array (namely map, filter etc) are functional and do not the change source array.
The array methods in javascript is broadly classified into three
- Mutator methods
- Accessor methods
- Iteration methods
Mutator methods - Ex : reverse(), push () etc : modify the array . As the name suggest these methods mutates the array on which they are called upon.
Accessor methods - Ex : include(), concat() etc : - do not modify the array and return some representation of the array.i.e a new array is returned which is modified array.
Iteration methods -Ex : filter(), map()- take as arguments functions to be called back while processing the array. In these methods the length of the array is already sampled/copied and the callback is performed on this arrary.
Generic methods (non-standard) - EX: join() These methods are generic in nature and is applicable to objects which “look like” Arrays.
The detailed explanation on this can be found in the below link :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype
Hope this helps!!!
Related
I wanted to know how javascript arrays work internally, I mean when for example you create a new array.
array = [1,2,3];
It's internally creating a new array: array = new Array();
and then calling Array.push() or similar?
Thanks.
The best resource to find out how javascript internals work is ECMAScript specification itself.
In order to understand what happens internally when you do array = [1, 2, 3] you would need to read section 7.3.16 CreateArrayFromList (elements). Roughly what happens is that first Array object gets created, then each element gets set to this object with CreateDataProperty (7.3.4 CreateDataProperty section) (DefineOwnProperty) internal method.
Then you want to learn what exactly happens when you push element to array. You check 22.1.3.17 Array.prototype.push ( ...items ) section for this. There you will find out that it uses quite different algorithm, namely it sets specific property of an object (7.3.3 Set (O, P, V, Throw) section).
So the answer is no, creating array like a = [1, 2, 3] does not uses same mechanics to insert items as push does. The first one roughly creates new property on (newly created) array object, the push sets property to existing object.
and then calling Array.push() or similar?
No. As you can see in the below example that when array is initialized push (overridden) method is not invoked.
Array.prototype.push = function(){ console.log("adding") };
var array = [1,2,3];
//console.log(array);
But, it is invoked when console statement is executed (after un-commenting the same).
var array = [1,2,3];
It doesn't call push but it's a simpler way to initialize an array when you know the elements in advance.
If you want to add another element you can do it in multiple ways like:
1) array.push(22); // adds integer 22 to the array
or
2) array[4] = 22; // adds integer 22 to the array at index 4
You can even do this:
array[20] = 22; // this will resize the array and keep all the uninitialized elements returns undefined. So array[10] is undefined, for ex.
If you want to know all the details about arrays explained in a simple way I recommend you the book: Secrets of the JavaScript Ninja. It has an entire chapter about arrays.
https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/1617292850/ref=sr_1_1?ie=UTF8&qid=1519484868&sr=8-1&keywords=javascript+ninja
I am reading info about array destructuring and the spread syntax from MDN and I have stumbled upon the following example that left me a bit cold, despite going over the material progressively.
The code giving me trouble is this:
function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
I get that v==-1; w==0; x==1; and y==2... but what is ...[3]?
The example is from the spread syntax hyperlink above.
The example you've found is a bit contrived, and you probably wouldn't see it in real code. It's just meant to illustrate that the spread argument syntax ... works with any iterable expression, including standard array literals like [1, 2, 3]. z will be 3 because
myFunction(-1, ...args, 2, ...[3]);
is equivalent to
myFunction(-1, ...args, 2, 3);
The ...[ and ] essentially have no effect in this case; the values are pulled out of the array so it's as though you'd just written them directly in the argument list. As another example,
myFunction(-1, ...args, 2, ...[3, 4, 5]);
is equivalent to
myFunction(-1, ...args, 2, 3, 4, 5);
although z would still be 3 (4 and 5 would be ignored because they're unexpected extra arguments).
Let's break this down a bit: the behaviour of the spread/rest syntax ... in an argument list is defined in section 12.3.6.1 "Runtime Semantics: ArgumentListEvaluation" of ECMAScript 6. To paraphrase, it essentially says "when you see ...X in an argument list, evaluate X, then go through the values it contains and add each of them as a separate argument".
So, when JavaScript sees , 3 in an argument list, it says "add 3 to the argument list".
But when JavaScript sees , ...[3] in an argument list, it says "create a new array containing 3, then go through each of its values (only 3) and add them to the argument list".
You're doing the same thing in both cases, but the simpler way is probably faster.
Please have a look at the below script. I am testing it with Chrome.
/*declare a new set*/
var items = new Set()
/*add an array by declaring as array type*/
var arr = [1,2,3,4];
items.add(arr);
/*print items*/
console.log(items); // Set {[1, 2, 3, 4]}
/*add an array directly as argument*/
items.add([5,6,7,8]);
/*print items*/
console.log(items); // Set {[1, 2, 3, 4], [5, 6, 7, 8]}
/*print type of items stored in Set*/
for (let item of items) console.log(typeof item); //object, object
/*check if item has array we declared as array type*/
console.log(items.has(arr)); // true
/*Now, check if item has array we added through arguments*/
console.log(items.has([5,6,7,8])); //false
/*Now, add same array again via argument*/
items.add([1,2,3,4]);
/*Set has duplicate items*/
console.log(items); // Set {[1, 2, 3, 4], [5, 6, 7, 8], [1, 2, 3, 4]}
Why it is returning false at items.has([5,6,7,8])?
Why it is allowing duplicate values? I thought "A set is in an ordered list of values that cannot contain duplicates"
How to access array added by items.add([5,6,7,8])?
Why it is returning false at items.has([5,6,7,8])?
From MDN
The Set object lets you store unique values of any type, whether primitive values or object references.
The objects are compared using the reference, not the value. Sets uses SameValueZero(x, y) comparison algorithm to compare values. It says Return true if x and y are the same Object value. Otherwise, return false.
Why it is allowing duplicate values? I thought "A set is in an ordered list of values that cannot contain duplicates"
Same as #1. An non-primitive value is said to be already exists in set if the same object(not just same looking) already added in the set.
How to access array added by items.add([5,6,7,8])?
You've to create a variable and add the variable to the set. Then this variable can be used to check if set has that array or not.
Quoting the specification:
Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set’s collection. Distinct values are discriminated using the SameValueZero comparison algorithm.
(emphasis mine)
The SameValueZero comparison algorithm handles any two arguments of the same type (where that type isn't null, undefined, a string, number, boolean, or Symbol) as follows:
Return true if x and y are the same Object value. Otherwise, return false.
Ultimately, what this means is that the objects are compared by reference (that is, effectively, "do these two objects point to the same location in memory?") Each time you create an array using either brackets ([]) or constructor invocation (new Array()) you create a new instance of Array in memory. When you keep a reference to the array, it will match (e. g. arr === arr). When you create new instances and compare them they will not match, even though their contents would compare as equal (e. g. [1, 2, 3] !== [1, 2, 3]).
Array comparision does not compare values it compares references so it is returning false. [1] === [1] will return false always.
See reference MDN
The Set object lets you store unique values of any type, whether primitive values or object references.
you are passing new object not reference so it is allowing to add duplicate. which is actually visually similar but reference are different.
Pass reference by assigning it to variable to access the added array.
var primes=[2,3,5,7]
primes.sync = function(){this[0]=23;}
primes // => [2, 3, 5, 7]
primes.sync()
primes // => [23, 3, 5, 7]
This seems to work perfectly in Chrome.
Are there any reasons to not use this syntax/"feature"? Also, can I count on primes to be behave as a normal array (e.g. when passing it to a function that expects an array)?
This is why I want to use it:
Say I have a peopleList in my program. Functions all over the app will use it like an array. Then, suddenly, I do a POST to the server. I then want the array to query the server directly, and update itself. This would allow for some very elegant code in my angular.js app.
The only trouble you'll likely have will be if you (incorrectly) try to use for-in to iterate the Array. As long as you use a for statement or one of the Array iterator methods to constrain the enumeration to numeric indices, there shouldn't be any trouble.
The Array will continue to behave like a typical Array.
What you would want to do is to add a function to Array.prototype, rather than adding it to an array instance. See below.
Array.prototype.sync = function(){this[0]=23;};
This way, all array instances, including those that have been initialized before adding the function, will automatically be able to use the function at once.
var a = [];
a.sync(); // TypeError: Object [object Array] has no method 'sync'
Array.prototype.sync = function(){this[0]=23;};
a.sync();
a // [23]
var b = [1,2,3];
b.sync();
b // [23, 2, 3]
However, only add those functions that are useful/meaningful/reusable to Array.prototype because it is going to be available for all array instances ever created and will be created.
If your function is going to be used by only few instances. You are better of adding them to each instance like you did above.
Please explain usage of _.identity(value) of underscore.js. Not able to understand it from the documentation ( http://underscorejs.org/#identity ).
Can you provide some example of its usage?
A JavaScript code pattern involving identity is filtering values based on truthiness, e.g.
var a = [null, null, [1,2,3], null, [10, 12], null];
a.filter(_.identity)
yields [Array[3], Array[2]].
Using
_.compact(a)
is clearer, but one may not use lodash or underscore at all, e.g.
function identity(x) {
return x;
}
a.filter(identity)
Whether it is a good code pattern is questionable for several reasons, but it's being used in the wild.
It is not a NOOP at all. A NOOP is an imperative construct in e.g. assembly, whereas in functional programming, it is like other functions in that it returns a value. If identity were a NOOP, then all pure functions could also be considered noop, and it would not be a sensible thing.
It's essentially a no-operation function. It returns the value of whatever was passed into it.
The part about it being used as a "default iterator" within the library itself means that in other functions which may have an optional "iterator" parameter (which is likely used as a function to apply to each element of an array of some kind), if no iterator parameter is passed, the library will use this "no-op" iterator instead and the elements of the array will remain unchanged.
A specific example:
Underscore.js defines _.each and as like this.
_.each = function(obj, iterator, context) {
...
}
This iterator shows el value. You maybe have used this idiom.
_.each([1, 2, 3], function(el){
console.log(el);
});
This iterator returns el value without change.
_.each([1, 2, 3], function(el){
return el;
});
The function that returns a value without change occur frequently. So Underscore.js wants to define the function. Underscore.js names the function _.identity.
_.identity = function(value) {
return value;
};
If Underscore.js wants to use a default iterator, all Underscore.js need is call _.identity.
_.each([1, 2, 3], _.identity);