When using the built-in methods available for arrays in Javascript, some methods will act directly on the calling array.
For example, myArray.sort(), will sort myArray in ascending order, alphabetically or numerically.
myArray.sort();
// sort() acts directly on myArray, changing it in its place thereafter
// ... also myArray.reverse() amongst others.
While other methods such as slice(), require there be something, either a variable or other output for it to return its value to...
var need_a_new_array = myArray.slice(10, 21);
// a new placeholder is needed for the results of slice... if not using
// the results immediately (i.e. passing to another function or
// outputting the results)
I was wondering what is the proper terminology for these methods and their differences. I am using arrays as an example here, but I'm sure that the
same probably holds true for objects in general.
I appreciate any help. Thank you.
The correct terms are mutator and accessor.
A mutator method mutates (changes) the object it is called on, while an accessor accesses (and returns) the value of the object it is called on.
You can see examples of the two types by looking at the method listing for Array.prototype. Note that they are divided into categories, two of which are Mutator methods ("These methods modify the array") and Accessor methods ("These methods do not modify the array and return some representation of the array.")
Mutators can not be called on immutable objects.
See also this related question on the software engineering SE: What is the term used to describe a function/method that modifies the object it's called on?
The terms you're looking for are 'immutable' and 'mutable' . Array.prototype.sort is a mutable method in that it 'mutates' (changes) the original array, where as Array.prototype.slice is immutable as it creates a new array with the result and leaves the original array intact.
Related
Are arrays merely objects in disguise? Why/why not? In what way(s) are they (such/not)?
I have always thought of arrays and objects in JS as essentially the same, primarily because accessing them is identical.
var obj = {'I': 'me'};
var arr = new Array();
arr['you'] = 'them';
console.log(obj.I);
console.log(arr.you);
console.log(obj['I']);
console.log(arr['you']);
Am I mislead/mistaken/wrong? What do I need to know about JS literals, primitives, and strings/objects/arrays/etc...?
Are arrays/objects merely strings in disguise? Why/why not? In what way(s) are they (such/not)?
Arrays are objects.
However, unlike regular objects, arrays have certain special features.
Arrays have an additional object in their prototype chain - namely Array.prototype. This object contains so-called Array methods which can be called on array instances. (List of methods is here: http://es5.github.com/#x15.4.4)
Arrays have a length property (which is live, ergo, it auto-updates) (Read here: http://es5.github.com/#x15.4.5.2)
Arrays have a special algorithm regarding defining new properties (Read here: http://es5.github.com/#x15.4.5.1). If you set a new property to an array and that property's name is a sting which can be coerced to an integer number (like '1', '2', '3', etc.) then the special algorithm applies (it is defined on p. 123 in the spec)
Other than these 3 things, arrays are just like regular objects.
Read about arrays in the spec: http://es5.github.com/#x15.4
Objects are an unordered map from string keys to values, arrays are an ordered list of values (with integer keys). That's the main difference. They're both non-primitive, as they're composed of multiple values, which also implies pass-by-reference in JavaScript.
Arrays are also a kind of object, though, so you can attach extra properties to them, access their prototype and so on.
In your revised example, you're only taking advantage of the fact that an array is actually an object, i.e. you can set any property on them. You shouldn't do that. If you don't need an ordered list of values, use a plain object.
Strings can be either primitive or objects, depending on how they were declared.
var str = 'yes';
Gives you a primitive, while,
var str = new String('yes');
will give you a String object.
All arrays are the same (Whether or not they were defined with [] or new Array()), are of the type object and inherit from the "Array" object's prototype. There aren't real classes in Javascript, everything is an object, and there's a system defined object called Array. It has a property called 'prototype' (of type object), and when you use the new keyword on an object with a prototype property, it creates an instance with a reference to the contents of the prototype and stores it in your variable. So all arrays you've ever used in Javascript are objects and instances of Array's prototype property.
In any case, although arrays really are objects, they behave like arrays because of their useful properties and functions (Such as length, slice, push etc).
Another note, although I said there are no classes, when you do this:
console.log(Object.prototype.toString.call(your_object));
it will give you a string in the form [object Object]. But what's useful is that when you call it with an array, you get [object Array] same with functions which give [object Function] and a number of other system defined types, which assists in differentiating between normal objects and arrays (Since the typeof operator will always just return the string 'object').
Try this
var a = Array;
and go into firebug and examine the contents of a, especially it's 'prototype' property.
Edit: Changed the wording a bit, to be more correct. In fact when you use the new keyword, it creates an instance which references the prototype object. So any changes made to the prototype after the instance's declaration, will still affect the instance.
Edit: In answer to your latest revised question (are arrays/objects actually strings in disguise): No. They are objects, as I've explained. Strings are either a primitive type, or an object type (An instance of the String object) which contains the primitive equivalent as one of it's properties.
Arrays are not primitives in Javascript, they are objects. The key difference is that as a result, when you pass an array to a function it is passed by reference, not by value.
So yes! Arrays are objects in javascript, with a full blown Array.prototype and everything (don't touch that though...)
The confusion comes from the fact that javascripts lets you access object attributes in two ways:
myObj.attribute
or
myObj["attribute"]
Really what makes an array an array has nothing to do with the way you store data -- any object can store values using the syntax you use to store the array -- what makes an array an array is the fact that array methods (e.g. shift() and sort()) are defined for Array.prototype.
Trying to be brief with what I believe to be of the most significance: arrays have a number of methods that objects do not. Including:
length
push
pop
An object declared as var x = {foo:bar} has no access to a .length() method. They are both objects but with the array as a sort of superset with methods mentioned as above.
I don't feel I this is even close to being of Crockford standard in terms of explanation but I'm trying to be succinct.
If you want to get some quick results, open up Firebug or your javascript Console and try Array.prototype and Object.prototype to see some details
Update: In your example you declare an array and then do:
foo['bar'] = 'unexpectedbehaviour';
will produce unexpected results and won't be available in simple loops such as:
var foo=[0,1];
foo['bar'] = 2;
for(var i=0;i<foo.length;i++){
console.log(foo[i]);
}
//outputs:
//0
//1
An array can accept foo['bar']=x or foo.bar=y like an object but won't necessarily be available to be looped through as highlighted above.
Not that I'm saying that you can't iterate through the properties of an object, just that when working with an Array, you're utilising that unique functionality and should remember not to get confused.
In JavaScript you have a few types, everything else is an object. The types in JavaScript are: boolean, number, and string. There are also two special values, "null" and "undefined".
So the quest "is a JavaScript array an object?" is slightly ambiguous. Yes, a JavaScript array is an "object" but it is not an instance of "Object". A JavaScript array is an instance of "Array". Although, all objects inherit from Object; you can view the inheritance chain on the MDC. Additionally, arrays have slightly different properties than an object. Arrays have the .length property. They also have the .slice(), .join(), etc methods.
Douglas Crockford provides a nice survey of the language's features. His survey discusses the differences you are asking about. Additionally, you can read more about the difference between literals and constructors in question #4559207.
Arrays are Objects, but of a specialized nature. Objects are collections of values indexed by keys (in Javascript notation, {'key': 'value'}), whereas Arrays are Objects whose keys are numeric (with a few functions and properties). The key difference between them is obvious when you use a for each loop--an Object will iterate over all the values in its properties, whereas an Array will return the keys instead. Here's a link to a JSFiddle demonstrating the difference--notice that the first for each, which uses an array, returns the indexes, not the values; in contrast, the second for each returns the actual values at those keys.
I'm reading Eloquent JavaScript's "The Iterator Interface" section and I have trouble understanding why we need to define MatrixIterator function to iterate Matrix's contents. I usually use for inside of another for to iterate the contents of a two-dimensional matrix, similar to this. Is it because the [Symbol.iterator]'s of each object in JS is used whenever you call the shorthand of for loop which is for...of?
Furthermore, am I correct to assume that the standard [Symbol.iterator] cannot automatically iterate a two-dimensional objects and thus, the need to create MatrixIterator and assigning it as:
Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
};
Are we allowed to do this instead: Matrix.prototype[Symbol.iterator] = MatrixIterator;?
Off-topic: I feel like I should know some other things more in-depth as I feel rather confused about these concepts. Could someone also elaborate what interface means? The book did mention it as:
Different pieces of such a program interact with each other through interfaces, limited sets of functions or bindings that provide useful functionality at a more abstract level, hiding their precise implementation.
and
Separating interface from implementation is a great idea. It is usually called encapsulation.
but it did not mention what implementation is.
the shorthand of for loop which is for...of?
No, for … of is not a shorthand for a normal for (…; …; …) loop. It's a completely separate mechanism.
Why do we need to define MatrixIterator function to iterate Matrix's contents?. Is it because the [Symbol.iterator]'s of each object in JS is used whenever you use for...of?
Yes. We define the MatrixIterator as it conforms to the iterator interface, as is expected to be returned by a Symbol.iterator method to be usable in such a loop.
Of course there are alternative ways to achieve this, we don't necessarily need to make an extra MatrixIterator class. A generator function is usually the easiest.
I usually use for inside of another for to iterate the contents of a two-dimensional matrix
Sure, but that's quite some syntactic overhead - two loops, two counters, double indentation. It could be much simpler, and we don't want to repeat this pattern everywhere when we want to iterate a matrix. The iterable interface allows this.
am I correct to assume that the standard [Symbol.iterator] cannot automatically iterate a two-dimensional objects
There is no standard Symbol.iterator method. Every type needs to define it itself. Array.prototype does, for example, but it works only on arrays not on our Matrix class.
Are we allowed to do this instead: Matrix.prototype[Symbol.iterator] = MatrixIterator;?
That doesn't work, as MatrixIterator a) is a constructor that needs to be invoked with new b) takes the matrix instance that it should iterate as an argument
I get that since javascript allows numeric keys for objects, the existence of array-like objects is therefore technically possible, but why did they ever become common?
Maybe the thought was that these array-like objects don't just have numeric keys, e.g. arguments has the callee property, so they can't be proper arrays to accommodate those properties.
But in javascript, it's perfectly valid to treat an array as an object and use non-numeric keys:
var myArguments = [];
myArguments[0] = 0;
myArguments['callee'] = function(){console.log('callee')};
If the object is array-like and would benefit from having access to the functions it would otherwise inherit from the array prototype, what would be advantage of making it an array-like object instead?
Edit: in case it wasn't clear in my question, an array like object would be something like the arguments object that has sequential numeric properties starting from zero, and has a length property that is one less than the highest numeric key. It also doesn't inherit from the array prototype, and doesn't provide access to array methods.
There is no big advantage, as you can access objects and arrays in very similar ways, array[number] for arrays, and object.propery, and object["property"] for objects. Arrays are better for storing data, not functions, in my opinion, and objects are better for storing sets of functions, in my opinion. For example, arrays could store your answers in a memory game, but an object would store the command for the game, like start or end.
I think the biggest advantage of using Array-Like object, is an object itself. An object has less overhead than an Array.
Can read: Array vs. Object efficiency in JavaScript
The other advantage of using objects, Language does not have to create/allocate contiguous memory as in Array. The object is more dynamic in nature. The array has to create a memory block in advance. and update on overload.
Read more: How are the JavaScript Arrays internally resizing?
Another factor, I think lookup in a map or object is faster.
Edit: As #Bergi stated, I forgot to add length property to the object. Also, I realized that my answer was not relevant. So I updated it after a short research.
In Typescript an arraylike object is defined like below:
interface ArrayLike<T> {
length: number;
[n: number]: T;
}
There are two main examples of array-like objects: arguments and HTMLCollection. The reason for using an array-like object is to use an array as only a data container without any behavior. As you could realize, if you try to use, say forEach, on arguments object you will get a type error since it's not defined on it. If you strictly believe that the array you are creating is only to be used as a data container without any methods whatsoever, then go with array-like objects. However, important to notice that they are getting scarcer day by day especially after ES6.
Actually Array in JS is an Object :)
As long as You need array, it is bad idea to create array-like objects, because JS engines optimize speed of arrays.
Yet, its possible.
Ref: MDN Maps
Use maps over objects when keys are unknown until run time, and when
all keys are the same type and all values are the same type.
Use objects when there is logic that operates on individual elements.
Question:
What is an applicable example of using Maps over objects? in particular, "when would keys be unknown until runtime?"
var myMap = new Map();
var keyObj = {},
keyFunc = function () { return 'hey'},
keyString = "a string";
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
console.log(myMap.get(keyFunc));
What is an applicable example of using Maps over objects?
I think you've given one good example already: You at least need to use Maps when you are using objects (including Function objects) as keys.
in particular, "when would keys be unknown until runtime?"
Whenever they are not known at compile time. In short, you should always use a Map when you need a key-value collection. A good indicator that you need a collection is when you add and remove values dynamically from the collection, and especially when you don't know those values beforehand (e.g. they're read from a database, input by the user, etc).
In contrast, you should be using objects when you know which and how many properties the object has while writing the code - when their shape is static. As #Felix has put it: when you need a record. A good indicator for needing that is when the fields have different types, and when you never need to use bracket notation (or expect a limited set of property names in it).
I think that with ES2015's Map only two reasons are left to use plain objects:
You don't want to iterate over the properties of an object type at all
or you do but the property order doesn't matter and you can distinguish the program from the data level when iterating
When is the property order unimportant?
if you have only a single value and some functions that should be associated explicitly with it (like Promise - which is a proxy for a future value - and then/catch)
if you have a struct/record-like data structure with a static set of properties known at "compile time" (usually structs/records aren't iterable)
In all other cases you might consider using Map, because it preserves property order and separates the program (all properties assigned to the Map object) from the data level (all entries in the Map itself).
What are the drawbacks of Map?
you lose the concise object literal syntax
you need custom replacers for JSON.stringyfy
you lose destructuring, which is more useful with static data structures anyway
Use maps over objects when keys are unknown until run time, and when all keys are the same type and all values are the same type.
I have no idea why someone would write something so obviously wrong. I have to say, people are finding more and more wrong and/or questionable content on MDN these days.
Nothing in that sentence is correct. The main reason to use maps is when you want object-valued keys. The idea that the values should be the same type is absurd--although they might be, of course. The idea that one shouldn't use objects when keys are unknown until run time is equally absurd.
One of the difference between Map and Object is:
Map can use complex data type as its key. like this:
const fn = function() {}
const m = new Map([[document.body, 'stackoverflow'], [fn, 'redis']]);
m.get(document.body) // 'stackoverflow'
m.get(fn) //'redis'
watch out: For complex data type, If you want to get the value, you must pass the same reference as the key.
Object, it only accept simple data type(number, string) as its key.
const a = {};
a[document.body] = 'stackoverflow';
console.log(a) //{[object HTMLBodyElement]: "stackoverflow"}
Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:
The keys of an Object are Strings and Symbols, whereas they can be
any value for a Map, including functions, objects, and any primitive.
The keys in Map are ordered while keys added to object are not. Thus,
when iterating over it, a Map object returns keys in order of
insertion.
You can get the size of a Map easily with the size property, while
the number of properties in an Object must be determined manually.
A Map is an iterable and can thus be directly iterated, whereas
iterating over an Object requires obtaining its keys in some fashion
and iterating over them.
An Object has a prototype, so there are default keys in the map that
could collide with your keys if you're not careful. As of ES5 this
can be bypassed by using map = Object.create(null), but this is
seldom done.
A Map may perform better in scenarios involving frequent addition and
removal of key pairs.
MDN
This question is a duplicate of but until it's closed, here's my answer from over there:
In addition to the other answers, I've found that Maps are more unwieldy and verbose to operate with than objects.
obj[key] += x
// vs.
map.set(map.get(key) + x)
This is important, because shorter code is faster to read, more directly expressive, and better kept in the programmer's head.
Another aspect: because set() returns the map, not the value, it's impossible to chain assignments.
foo = obj[key] = x; // Does what you expect
foo = map.set(key, x) // foo !== x; foo === map
Debugging maps is also more painful. Below, you can't actually see what keys are in the map. You'd have to write code to do that.
Objects can be evaluated by any IDE:
I know that Javascript Arrays are actually objects, and because they are objects, they can have properties. Here's an example:
var a = [1, 2, 3];
a.currentIndex = 2;
a.next = function() { ... };
a.prev = function() { ... };
a.length // returns 3
To me this seems like it could come in very handy. I see numerous reasons why you might want to store state or utility functions on the actual array itself and not on some other variable. It even seems better than having the array as a property of an object with the other stuff stored on that object.
Here's my question:
Does anyone know of any issues with storing properties on a Javascript array? Does it work in all browsers? Is there any evidence that this will change with future versions of Javascript? Is there any general wisdom about whether or not it's a good practice?
(p.s. For the record, I don't need to iterate over the array with a for...in loop. I understand that such a loop would include the properties as well)
Since you already ruled out the for in issue, my answer here is a clear "no" - there is no issue to worry about. All Array.prototype methods will only apply on the "indexed" keys (0...n).
The best example here is the well know jQuery library, it also uses Array-Like objects to store DOM nodes on but it also has lots of methods which are attached to that object (jQuery uses the prototype there tho). However, other librarys like Zepto, just put those methods directly on the "array" object itself.
So again, no there is no other caveat and you're save doing it.
Just throwing out one more thing -- None of the "copying" array prototype functions will copy any of your extra properties, if that's important, i.e. .splice, .slice, concat will give you new "clean" arrays without currentIndex, etc
Yes it works in all browsers. And it is valid javascript (arrays are objects). One could think of a number of reasons why you want to use the Array constructor instead of an object, but if this is your preferred coding style, go with it.