Attaching a function to an array - javascript

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.

Related

Javascript Arrays - How do they work internally?

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

Why some methods in JavaScript make a copy of the object invoked?

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!!!

Is there something wrong with using window['variableAsString']?

It's been very useful for writing functions that require iterating through variables, but I've heard users advising against using similar methods.
Is there something wrong with writing code this way?
Example of use:
Say I have three arrays and want to add a value to all three:
array1=[];
array2=[1,2];
array3=[4,4];
for (var i=1;i< 4; i++){
window['array'+i].push(1);
}
If you're going to need to iterate through a set of variables, plan ahead and put them inside an object:
var myArrays = {
array1: [],
array2: [1, 2],
array3: [4, 4],
"any-name-you-want-works": []
};
Accessing them is still straightforward:
myArrays.array1.push(1);
myArrays["any-name-you-want-works"].push(1);
Adding a new array to myArrays:
myArrays.array4 = [3, 5];
And iterating is made easy (this is the recommended way):
for (var arr in myArrays) {
if (object.hasOwnProperty(arr)) {
arr.push(1);
}
}
If you don't need to support old browsers, you can use newer features as well (source):
Object.keys(myArrays).forEach(function(key, index) {
myArrays[key].push(1);
});
More info on Object.keys().
Then, if you're using a popular lib like underscore or lodash, you can do lots of thing easily on objects and arrays:
_.chain(myArrays)
.each(function(arr) { arr.push(1) }) // adds one to each array
.first() // takes the first array
.map(function(val) { return val *2; }) // and multiply each value
.reverse() // then reverse those values
.value(); // and get the array
As I mentioned in a comment, you shouldn't pollute the global window object and also iterate through it to avoid collision or unexpected behaviors.
Wrapping your code inside an IIFE (Immediatly-Invoked Function Expression) is always a good idea, instead of using the global scope, for multiple reasons, but that's a whole other discussion and we can achieve what you want without it (for this question anyway).
It's not good practice to stick all of your variables in the global scope. There are certain cases where it's acceptable, for instance if you have a library that you want to be available everywhere, but it's generally best to avoid using window, particularly in the way you are using it. The good news is you don't need window to do what you want to at all. You can just add your arrays as attributes on an object, and reference them using strings the same way you did with window (which is just a globally scoped object). Note the use of var to scope the object locally.
var arrays = {
array1: [],
array2: [1,2],
array3: [4,4]
};
for (var i=1;i< 4; i++){
arrays['array'+i].push(1);
}

how can I concatenate two arrays in javascript?

I saw this response for extending one array with another
so I tried:
console.log(['a', 'b'].push.apply(['c', 'd']));
but it prints:
2
shouldn't it print:
['a', 'b', 'c', 'd']
if not what was i doing wrong?
if not what was i doing wrong?
First of all, .push returns the new length of the array:
var arr = [1, 1, 1];
console.log(arr.push(1)); // 4
console.log(arr); // [1, 1, 1, 1]
Second, .apply needs two arguments: The object you want to apply the function to, and an array of arguments. You only pass a single argument, so your code is basically equivalent to:
['c', 'd'].push()
I.e. you are not adding anything to the ['c', 'd'] array. That also explains why you see 2 in the output: ['c', 'd'] has length 2 and .push() doesn't add any elements to it so it still has length 2.
If you want to use .push to mutate the original array (instead of creating a new one like .concat does), it would have to look like:
var arr = ['a', 'b'];
arr.push.apply(arr, ['c', 'd']); // equivalent to Array.prototype.push.apply(arr, [...])
// ^ ^--------^
// apply to arr arguments
console.log(arr); // ['a', 'b', 'c', 'd']
See also
How to append something to an array?
Javascript push array values into another array
If your scenario allows for ES6 syntax, you can use the spread element to concatenate arrays:
var arr1 = [1,2];
var arr2 = [3,4];
var arr3 = [...arr1, ...arr2];
console.log(arr3); // Logs [1,2,3,4]
Try this:
console.log(['a', 'b'].concat(['c', 'd']));
If you're using a modern browser then you can use the spread operator, ... like so:
console.log([...['a', 'b'], ...['c', 'd']])
If you're using an older browser/no transpiler then concat is the way to go:
console.log(['a', 'b'].concat(['c', 'd']))
As there seems to be a general misunderstanding here about the use of various array appending methods I will add to it with the safest alternative to ES6's introduction of the spread operator
Functional language
Functional languages were developed to simplify code development allowing programmers and systems engineers to define functions to replace common operations/algorithms.
JS is a functional language, you write functions to make life easier, save you typing and making the program easier to test and validate, so why fight it with dangerous alternatives .
The mistake being made in the answers (with the exception of Jonathan Michalik answer) is they are all trying to either coerce the language into doing something that should be done by a function (Function.apply), or providing a solution without explanation of what it is doing (Array.concat).
Write a function to append arrays, for example.
function appendArray(array,array1){ // adds the items in array1
// array1 can be any type
// to the end of array
var len,i;
array1 = [].concat(array1); // ensure array1 is an array
len = array1.length;
i = 0;
while(i < len){
array[array.length] = array1[i++]; // this is 2* quicker than invoking push
}
return array;
}
For the 60 seconds it takes to write, or less to include as part of a general library you have created a safe and general purpose array append function.
log(appendArray([0, 1, 2, 3], 4)); // 0,1,2,3,4
log(appendArray([0, 1, 2, 3], [4, 5, 6])); // 0,1,2,3,4,5,6
Now to why the other answers are not good solutions.
Array.concat
is the safer option, and should be used if you are in control of all references to the array. It suffers from the fact that it creates a new array when it is used.
var array1 = [1,2,3];
var array2 = [4,5,6];
array1 = array1.concat(array2); // 1,2,3,4,5,6
Many are under the mistaken belief that the concat( array2 ) is added to array1, it is not, it is added to a copy of array1. If you have other references to the original array then you end up creating a copy of the concatenated array, while the original array remains in memory.
function doSomething(array){
... generate some info
array.concat(info);
}
var myArray = [...someData]; // the array
var myObject.setArray(myArray); // create a new referance to the array in myObject
myArray = doSomething(myArray); // adds info via concat to the array.
It is not immediately apparent, but you now have two arrays the original inside myObject, and the concatenated version in myObject which may store the array referance in closure, that you can not change or delete unless you dereference all associated context. Nor do you know how many references to the original array exist.
Function.apply()
There are many reasons not to use this function but of particular note is using it on the Array.push function. It suffers from indeterminacy if you use it without type checking the arrays.
var myArray = [1,2,3,4];
var data = getSomeData(); // may return array or array like data
myArray = doSomeWorkOn(myArray); // the spec says this may return undefined
myArray.push.apply(myArray,data); // what happen here is indeterminate and
// depends on the JS context, the JS mode
// and the JS version
Without vetting myArray for undefined you can not predict what the result of apply will be. It could throw and error, or it may silently fail if the global scope has a push function and you are not in strict mode.
Without knowing the JS version ES5 or ES6 the apply may have pushed one object data onto myArray, or all the items in data ES5 does not recognise array like objects as arrays for Function.apply while ES6 does. So you must vet myArray, or data to be of the correct type for the current JS version.
Arguments have limits that are dependent on the current JS context, there is also inconsistency between JS context's handling of long argument lists. Some will throw an error, others will simply truncate the arguments.
var myPixelData = [];
myPixelData.push.apply(myPixelData,getSomePixelData()); // what will happen????
This may throw, or it may work, or it may have only pushed 65535 items onto the array, or more, or less. You will have to wrap it in a try catch, and vet the results against the length of the array returned by getPixelData. All the added complexity just to make it easier???
So why use Function.apply on Array.push or use Array.concat when it is so much easier to write a function to do what you want and save you from a long list of problems, ambiguities, and errors.
You should try this:
a= ['a', 'b'];
b= ['c', 'd'];
a.push.apply(a,b);
console.log(a);
you should use variables like so
<script type="text/javascript">
var arr1=['a', 'b'];
var arr2=['c', 'd'];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1);
</script>

Array reference versus value semantics

Any programming language which provides arrays (lists, vectors, tuples etc.) must decide whether they have reference or value semantics, with the usual/obvious choice being reference semantics for mutable arrays and value semantics for immutable ones.
JavaScript which provides mutable arrays appears to have chosen reference semantics e.g. given
var a = [1, 2, 3]
var b = [1, 2, 3]
then a != b, as expected because though they have the same contents, they are different arrays.
However when you use them as keys in an object, the picture changes; if you set obj[a] to a value, then obj[b] gets the same value. Furthermore, this remains true if you change the contents of the arrays; at least when I tested it in Rhino, it behaves as though the interpreter were recursively comparing the full contents of the supplied and stored key arrays on every lookup, complete with a check for the infinite loop that would occur if one of the arrays were made to point to itself.
Is this the intended/specified behavior in all implementations?
Does it also apply to objects used as keys?
Is there any way to get the other behavior, i.e. to look up values using arrays as keys with reference semantics?
When arrays are used as property names they are cast to a string:
[1,2,3].toString() == '1,2,3'
Once turned into a string value, arrays with the same contents would map to the same property.
To answer your last question, you can't use objects to reference property names (keys) whereby only the same object maps to the same property (1:1 mapping).
obj[a] and obj[b] will run the toString function on the arrays and produce the same result for both. It doesn't try to use the arrays as keys.
var a = [1,2,3];
var x = {};
x[a] = "test";
var i;
for(i in x)
{
alert(i); //"1,2,3"
}
jsFiddle example

Categories

Resources