I'm a bit confused by the Array.length property (i.e. a property named length on the Array function object) and array_instance.length (i.e. a property named length on instance of array object)
So what is difference between the two length property and when should/shouldn't we use them?
Edit 1:
there is also a length property on Array.prototype object. I am so confused.
Edit 2
Just to paint a clearer picture, here are the different length properties I have found
Edit 3
This is one of the follow up questions I asked in the comment section, but I think the question is important to fully understanding length property, so I have copy-pasted here in the main section
Follow up question:
Array.prototype.hasOwnProperty('length') and Array_instance.hasOwnProperty('length') return true, does that mean there are two length properties, one on array_instance, one on Array.prototype object, with the array_instance.length overshadowning the Array.prototype.length?
functions have a .length property which corresponds to how many arguments they are expecting. For example:
const unary = (a) => {
}
const binary = (a, b) => {
}
console.log(unary.length);
console.log(binary.length);
So the Array constructor has a length of 1 because it expects one parameter to be passed to it (namely, the size of the array).
array objects also have a .lengthproperty, which is unrelated other than having the same name. This property says how large the array currently is.
I really think a lot of the other answers have covered everything needed here, but as it seems the OP hasn't had what they see a a clear answer, I will try to set everything out, somewhat extensively - but as clearly as I can - in order to clarify. (Apologies if anyone thinks I am "stealing" their answer - I assure you that this is not the intention, and I'm deliberately not looking at them as I type this, but I've certainly read most of them and even upvoted a few.)
Array.length
This has already been well-covered above. Array is a native JS function, which you can use for creating arrays. It's less common then simply defining an array literal (and as far as I know there is no reason it would ever be preferable), but instead of var a = [1,2,3] you are allowed to do this:
var a = Array(1,2,3);
console.log(a);
Note in passing that you don't want to do this to create a singleton array, there is an utterly mad gotcha of a special case when you supply exactly one parameter which happens to be an integer:
var a = Array(5);
console.log(a);
Although that shows what appears to be an array of 5 undefined values in whatever JS console implementation SO uses, that's not actually quite what has been created (nor what is displayed in the current version of Chrome). I'm getting way off-topic but I'll refer you to Kyle Simpson's excellent walkthrough of the madness.
Anyway, since Array is a function, as others have already observed, it has a length property as all functions do. I'm really not sure why it evaluates to 1 though - for a user-defined function it is the number of arguments the function was formally declared with, but since Array like all native JS functions isn't actually implemented in JS, I couldn't tell you how the implementation actually works. Clearly it can take any number of arguments and thus is a rather "exotic" function. I don't think that Array.length would ever be useful, no matter what value was arbitrarily assigned to it, but it seems that most implementations go for 1, even if the specification leaves it open. (I'm not enough of a spec devotee to know if this is actually defined in there or left up to implementations.)
arrayinstance.length
This is just the feature of arrays that we know and use all the time. All JS arrays get this property - note that, although it is a property rather than a method (that is, it is not a function), it nevertheless "auto-updates" as the array gets longer/shorter:
var a = [1,2,3];
console.log(a.length);
a.push(4);
console.log(a.length);
a.pop();
a.pop();
console.log(a);
console.log(a.length);
Although as I said, Javascript's native constructors are not implemented in terms of JS itself, you could implement this kind of behaviour by defining a getter (at least since ES5).
Array.prototype.length
To fully explain what Array.prototype (and similar objects) is would take me deep into how Javascript's object system work. Suffice to say here that, unlike class-based languages (and JS does not have classes, even in ES6, despite the class syntax allowing us often to pretend it does), JS does not have the usual concept of "inheritance" that so-called OO languages do. In JS's version (sometimes called "prototypal inheritance"), what happens it that each object has an "internal prototype" which references some other object - and if you try to access a property on that object which it doesn't have, the JS engine will look at that "prototype object" for the property and use its value instead.
It's actually a very simple mechanism, but there are a number of things in the language which confuse this, one of them being the fact that functions (which are also objects) have a property called prototype - which does not point to the real "prototype" object which gets consulted if a nonexistent property is referenced on the function object. (A normal function foo has Function.prototype is the object that it delegates to - not foo.prototype.) However, if you declare a function foo, an object called foo.prototype is created - which is basically an empty, nondescript object. Its significance is that if the function foo is used as a "constructor" - that is, if you make an object by calling new foo() - foo.prototype will then be the object that JS will look up properties (including methods) on if any object constructed from foo happens to fail a property lookup.
This is why, at least in pre-ES6 code, you quite frequently saw this kind of pattern:
function foo(a,b) {
this.a = a;
this.b = b;
}
foo.prototype.add = function() {
this.a = this.a + this.b;
}
var myFoo = new foo(1,2);
console.log(myFoo.a);
myFoo.add();
console.log(myFoo.a);
myFoo.add();
console.log(myFoo.a);
console.log(myFoo.hasOwnProperty("add"));
Despite appearances, myFoo doesn't actually have a method add in this example - as confirmed by the final console.log. When the JS engine fails to find the property though, it goes to myFoo's "internal prototype", which happens to be foo.prototype. And that's why the method works, as it would on any object constructed from foo.
Anyway, this is leading up to the fact that Arrays, which could be (although almost never are) constructed by calling new Array (I didn't use the new operator above, but I could have done, this is a case where it makes no difference), therefore delegate to Array.prototype. All those array methods that you know and love don't "really" exist on the arrays you call them on:
var a = [1,2,3];
a.push(4);
console.log(a);
console.log(a.hasOwnProperty("push"));
console.log(Array.prototype.hasOwnProperty("push"));
So array methods only work because those methods are actually found on the Array.prototype object, to which all arrays delegate for property/method access if the lookup doesn't succeed on the array itself. (And this is why, if you look up any of them on MDN, the top of the page always says Array.prototype.<method_name>, because that's where the method "really" lives.)
A drastic demonstration of this (please DON'T do this in production code!)
// you're used to doing this, and it works:
[1,2].push(3);
console.log("that went fine");
// vandalism on a grand scale!
Array.prototype.push = undefined;
// now you can'tse the "push" method anymore!
[1,2,3].push(4);
But I'm going to end on something of an anticlimax. The above is true for array methods - but the length array property isn't a method. As observed above, it's just a "plain" (non-function) property, which "magically" behaves somewhat like a function call. As observed in the OP, .length property accesses don't delegate as the method calls shown above do, the property exists on each array in itself.
So why does Array.prototype still itself have a length property? Well, Array.prototype is actually itself an array. In fact, that's not the only thing:
Array.prototype.push(1);
console.log(Array.prototype);
Function.prototype();
notice that Array.prototype.push(1) ends up with Array.prototype being the singleton array [1]. So Array.prototype is "kind of like" the empty array (it's not exactly the same, because it has all those methods mentioned above directly accessible on the object itself, which a "normal" empty array doesn't). And with Function.prototype, although calling it didn't output anything, the fact that no TypeError was raised proves that it really is a function (it's actually a "no-op" function, like function() {}, but once again with various methods diretcly on it - the methods which every function has access to, such as .call and .bind).
Anyway, to cut the digression short, since Array.prototype is - as far as ordinary array properties are concerned, at least - an empty array, this explains why it has a length property, and why it's equal to 0.
I hope this clears things up, as well as demonstrating some of the more intriguing parts of Javascript.
The first part has already been answered, Array constructor is a function and functions have a .length property.
For the second, Array.prototype.length, it's a bit more obscur...
Array.prototype is actually an Array:
console.log(Array.isArray(Array.prototype)); // true
Array.prototype.push('hello');
console.log(Array.prototype.length); // 1
console.log(Array.prototype[0]); // "hello"
As to why is it an Array? Because specs say so:
The Array prototype object is an Array exotic objects and has the internal methods specified for such objects. It has a length property whose initial value is 0 and whose attributes are { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }..
There is also a note specifying that it is for compatibility reasons with previous versions of the specs.
As to why is was designed as being an Array? I have no really strong idea...
Disclaimer:
I have tried to show how constructor and instance works in general. But in fact, they have huge difference between different constructors.
Any constructor has been set its length with the value specified by the spec. Specially most of them are set to 1:
Number.length // 1
Object.length // 1
Function.length // 1
Array.length // 1
// etc.
Similarly,
Array.constructor.length // 1
// etc.
Like #Kaiido pointed out in the comment, you can see some constructor length is set:
Document.length // 0
Int8Array.length // 3
Date.length // 7
And you may also notice the length of the instance is undefined as they are not set. - It's out of the scope though.
let d = new Date
d.length === undefined // true
See at the bottom for relevant references.
But when you have an instance of it, then you're creating a new value of it:
typeof new Array === typeof Array.prototype // true
typeof new Function === typeof Function.prototype // true
// etc.
So, when you use an instance it has no length value set because it has no any parameters:
let arr = new Array // Array.prototype
arr.length === 0 // true
But when you use an instance with parameter, then you have the length property with the value of parameters
let arr = new Array(20)
arr.length === 20 // true
let func = function(a1,a2,a3,a4){}
func.length === 4 // true
// etc.
So now, you have been wondering why the constructor has length value equal to 1?
It's because the spec has set the value to be 1 initially.
Every built-in Function object, including constructors, has a length property whose value is an integer. Unless otherwise specified, this value is equal to the largest number of named arguments shown in the subclause headings for the function description, including optional parameters.
The value of the [[Prototype]] internal slot of the Object constructor is the intrinsic object %FunctionPrototype%.
Besides the length property (whose value is 1),
See these references:
Standard built in objects,
19.1.2 Properties of the Object Constructor,
19.2.2 Properties of the Function Constructor,
etc.
Also, you can see the prototype has length to 0, you already know it why in the preceding example.
Though, here's just a reference stating that:
19.2.3 Properties of the Function Prototype Object
And there some constructor whose length is set different. This is the out of scope of this answer. Though, here's a reference for the date constructor:
20.3.3 Properties of the Date Constructor
So, it's totally up to the spec how they have been defined.
Array.length
Array is constructor which means its type is "function". You try the checking it console.
typeof Array //"function"
According to MDN
The length property indicates the number of parameters expected by the function.
As the Array function expects single argument so Array.length = 0
array_instance.length
The length property of an object which is an instance of type Array sets or returns the number of elements in that array
As we know that arrays are actually objects so objects can have properties. The property length is on the instance of array.
Now second question you may ask why we don't get the length properties of array using Object.keys or for..in loop. The answer is because this property is not Enumerable.
let arr= [];
//this.property 'length2' will not be shown in for..in or Object.keys();
Object.defineProperty(arr,'length2',{
value:'length2 for test',
enumerable:false
})
//this property 'x' will be displayed in for..in loop and Object.keys()
Object.defineProperty(arr,'x',{
value:'length2 for test',
enumerable:true
})
console.log(Object.keys(arr)); //["x"]
Array.prototpe.length
According to the DOCS
The initial value of Array.prototype.constructor is the standard built-in Array
The Array prototype object is itself an array; its [[Class]] is "Array", and it has a length property (whose initial value is +0) constructor
Actually Array.prototype is an array. And remember array is always object. So it can have properties. The methods of the Array are stored in form of key:value. and there is no element in that array so it Array.prototype.length returns 0. If you push() some elements into it you will see it as array.
console.log(Array.prototype.length) //0
console.log(Array.isArray(Array.prototype)) //true
//adding element to array
Array.prototype.push('x')
console.log(Array.prototype.length) //1
As I explained in second parts you can hide properties of Object by setting enumerable:false. All the methods are keys of Array.prototype But now shown in for..in loops.
Array.length
For the number of properties in the array or the length property of an object which is an instance of type Array sets or returns the number of elements in that array.
Array.prototype.length
Inherited number of properties in the array. When you check Array.length you're actually checking Array.prototype.length
I am trying to understand something in JavaScript.
Lets say I do the following:
let x = [];
x['Monday'] = 'Work';
x['Tuesday'] = 'More Work';
console.log(x) //[Monday: "Work", Tuesday: "More Work"]
console.log(x.Monday) //"Work"
console.log(x.Tuesday) //"More Work"
Can someone help explain why the array now behaves like an Object?
Because array IS an Object
[] instanceof Object
> true
[] instanceof Array
> true
Both give you true because Array extends normal Object functionality. Furthermore, the array indexes all its members by string values
const array = [];
array[0] = 1;
array['0'] // will give you 1
which is also unexpected by most of the people. So, the array behaves like an Object even if you use it as a normal array with indices
Everything in JS is an Object except the Primitive types (and even those come with Object wrappers)
That is why you can access properties and methods like array.length array.indexOf() etc just like you would with any other object. The indices are like special properties on the array object which are iterable
over all JavaScript array is also an object
if you check the type of x
typeof x
it will prints "object"
var x =[];
x.name = "X";
here we are creating a property "name" of x array
Because in javascript there is no real Array type as a low level type. Array class is just a kind of a class which extended from Object class.
Every class in javascript except primitives like Number, String ect. are extended from Object class.
Every object (instance of class) can have properties and methods.
Every instance of classes can have new properties and methods on the run-time as well as this properties could be changed modified or deleted.
So by doing this.
var ar = [] // I used array literal. Not totally same but similar of doing new Array();
arr['foo'] = 'bar';
You are just adding a new property to an object.
I think you’ve heard by now that Array is a type of Object, so I won’t beat that dead horse any further. Well maybe a little.
Which means you can accsss and set properties with names like “Monday” and “Tuesday”. Since it’s a type of Object this is no problem (and a statement like a[‘1’] is implicitly converting that string ‘1’ to a number 1). But don’t mistake this property name to be an array index, those are specifically integers**. What’s the importance of that?
Arrays have a nice behaviour that when you use an actual array index, the length property is automatically updated for you. Not to mention array index access is usually optimized by JVM implementations to be noticeably faster than regular property accesses.
Also let’s not forget one of the other huge benefits of having Arrays, they inherit from the Array.prototype, which has a whole lot of useful methods :)!
So in short use Arrays like Arrays — gain the perf boost while also making sure the code is easier to comprehend.
Source: JS Definitive Guide 6th - Array chapter
(** non-negative and less than 2^32-2 since arrays has 32-bit size indexes.)
With prototype inheritance in ES5, it looks not trivial to inherit from Array and get the expected behavior, like updating .length automatically when adding item to Array (see below code). ES5 creates object of the derived function (MyArray) then pass it base type to do initialization, why is this model hard to get the expected behavior in this model?
ES6 changed the behavior and creating object in base constructor, and constructor of derived class initialize it after that (after call to super()), wondering why this solved the problem.
function MyArray(){}
MyArray.prototype = Object.create(Array.prototype);
var myArr = new MyArray();
myArr[0] = 'first';
console.log(myArr.length); // expect '1', but got '0' in output
The key thing about Array is that a real array object is an Array Exotic Object. An exotic object is an object that has behavior that could not be achived using standard JS language features, though in ES6 Proxy allows much more ability for user code to create exotic-like objects.
When subclassing a constructor that returns an exotic object like Array, the subclassing method needs to be done in such a way that the object created is actually an exotic object. When you do something like
function ArraySubclass(){}
ArraySubclass.prototype = Object.create(Array.prototype);
then
(new ArraySubclass()) instanceof Array
because the prototype matches up, but the object returned by new ArraySubclass is just a normal object that happens to have Array.prototype in its prototype chain. But you'll notice that
Array.isArray(new ArraySubclass()); // false
because the object isn't a real exotic. In this case
new ArraySubclass()
is identical to doing
var obj = Object.create(ArraySubclass.prototype);
ArraySubclass.call(obj);
So in ES5 how do you extend Array? You need to create an exotic object, but you also need to ensure that the exotic object has your ArraySubclass.prototype object in its prototype chain. That is where ES5 hit it issues, because in vanilla ES5, there is no way to change an existing object's prototype. With the __proto__ extension that many engines added you could get the correct Array subclassing behavior with code like
var obj = new Array();
obj.__proto__ = ArraySubclass.prototype;
ArraySubclass.call(obj);
Say you wanted to generalize the pattern above, how would you do it?
function makeSubclass(baseConstructor, childConstructor){
var obj = new baseConstructor();
obj.__proto__ = childConstructor.prototype;
return obj;
}
function ArraySubclass(){
var arr = makeSubclass(Array, ArraySubclass);
// do initialization stuff and use 'arr' like 'this'
return arr;
}
ArraySubclass.prototype = Object.create(Array.prototype);
so that works in ES5 + __proto__, but what about as things get more complicated? What if you want to subclass ArraySubclass? You'd have to be able to change the second the second parameter of makeSubclass. But how do we do that? What is the actual goal here? When you do something like
new ArraySubclass()
it is the value passed to new that we care about as that second parameter, and it is that constructor's prototype that should be getting passed along. There is no nice avenue in ES5 to accomplish this.
This is where ES6 classes have a benefit.
class ArraySubclass extends Array {
constructor(){
super();
}
}
The key thing is that when super() runs, it knows that ArraySubclass is the child class. When super() calls new Array, it also passes along an extra hidden parameter that says "hey, when you create this array, set its prototype to ArraySubclass.prototype. If there are many levels of inheritance, it will pass along the child-most prototype so that the returned exotic object is a real exotic while also making sure it has the correct prototype.
Not only does this mean that things are constructed properly, but it means that engines can create the object with the correct prototype value up front. Mutating an object's __proto__ value after creating is a well-known deoptimization point because of the ways engines process and track objects.
I'm currently making a chat AI that uses markov chains. One of the backend objects is an object that maps a string to any chain that it is a part of, such that:
var words = {}
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ]
Now, I have a problem. 'constructor' is a perfectly valid word, but it appears as though you can't map it as I have described above, as 'constructor' is a property of every object in JS (along with other things, of course). So to my question: is there a way to construct this map?
constructor isn't a property of every object. Though by default every object created from literal syntax will have constructor in its prototype chain. This is because those objects inherit from Object.prototype, which does have that property.
One solution is to use objects that have no prototype chain.
var words = Object.create(null);
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ];
Now there will be no inherited properties to confuse things. This won't work in IE8 and lower though.
Another solution is to use .hasOwnProperty() to see if the constructor property is on the object itself or inherited. If inherited, then it's not the one you want.
if (words.hasOwnProperty("constructor"))
console.log(words.constructor);
If the condition passes, then we know we're not using the inherited property, but rather the object's own property.
Consider the below code.
I created an empty object. And am adding var1,var2,var3 without declaring them.
var har = new Object();
har.var1 = "Var1";
har.var2 = "Var1";
har.var3 = "Var1";
alert( har.var1 );
If I can do this, then why do I need to create a Class and fix the attributes when I can introduce new attributes anytime?
Why would you even need to use objects in the first place? Non-object-oriented languages are already Turing-complete, so everything you can accomplish with objects you can also do without them.
What you're showing in your example is not really an object, but just a dictionary. In cases like this, where you only need to keep several related properties together, anonymous unprototyped objects like the one you're using are the de-facto standard approach (though it is customary to initialize them with the shorthand syntax, e.g. var har = {}). It is an object, since it uses the object data structure, but it is not object-oriented.
Actual objects, in contrast, not only define data, but also the operations that you can perform on that data. Objects have not only properties, but also methods which work on these properties. These properties are usually defined in the object prototype (which you're calling "class", but Javascript is not a class-based language, but a prototype-based one). All methods defined in the prototype are shared between all instances of that prototype.
function Counter() {
this.counter = 0;
}
Counter.prototype.increment = function() {
this.counter++;
alert(this.counter);
}
var c1 = new Counter();
var c2 = new Counter();
c1.increment(); // alerts 1
c1.increment(); // alerts 2
c2.increment(); // independent from c1: alerts 1 again
Each instance is still a dictionary, as in your example (and you can even still add more properties to them, they are not "fixed" by having a constructor and prototype), but now it can also perform tasks on its properties. This can be done your way as well:
c1 = {
counter: 0,
increment: function() {
this.counter++;
alert(this.counter);
}
}
c2 = {
counter: 0,
increment: function() {
this.counter++;
alert(this.counter);
}
}
You can see, however, that if you need two counters, without using prototypes you will need to duplicate your entire object definition. This will be cumbersome to write and maintain, and each increment function will be defined separately, thus it will also waste memory.
That said, in cases where you need an object that you know you'll only ever need one instance of, it makes no sense to define a constructor and a prototype for it. Such objects are usually regarded as namespaces instead of actual objects.
Appendix: Dictionaries vs Objects
A dictionary is a data structure which holds named elements. Besides "dictionary", they are also called associative arrays or hashmaps. Objects in Javascript are implemented as dictionaries — each property is a named element in the dictionary. In addition to a plain dictionary, objects also have a prototype, which is kind-of like a parent object — when you look up a named element in the dictionary and it is not there, it is automatically searched for in the prototype as well. This way, default properties and methods are defined only once in the prototype, and do not need to be copied into each instance. (The prototype of an object is often visible as the instance.__proto__ property, though this is non-standard and deprecated even in browsers that support it; the actual prototype is defined as an internal, non-accessible property by the standard)
So, Javascript objects are actually dictionaries. When you want to use a plain dictionary to store some related properties together, there is no separate syntax in Javascript to create a dictionary that is not an object, so you create an instance of the base Object type to hold your dictionary (it does not matter if you do var dict = new Object or var dict = {}, the result is the same); thus, dictionaries that you use in your code are also objects.