Why no () after .length in Javascript? - javascript

I'm a hobby programmer, I've studied several languages and almost always find that 'length' is a method/function. I've been trained, from what I can tell, that any method call must be called with a parenthesis after, even with no arguments.
Not so in Javascript.... Why?
C# .length()
ROR .lenth()
etc...

In Javascript it's a property, not a function.
In languages like C where the length function has to loop through the entire string to find the end, it's logical to have it as a function (as it does some work). In languages where the length is kept as a separate value, it's logical to have it as a property instead (as it only reads an already existing value).

It's because it's a property, not a function.
You can find the relevant documentation here at MDN.
This property returns the number of code units in the string.

The name length is a property not a function. A property can be accessed by name while a function must be invoked with () and possibly arguments to produce a value.
For example
var x = {};
x.name = "example"; // Property
console.log(x.name); // Prints "example"
x.getName = function() { return "example"; } // Function
console.log(x.getName); // Doesn't print what you'd want here
console.log(x.getName()); // Prints "example"

In JavaScript, .length is a property of the array, not a function of the prototype. Imagine your array is represented like this:
array {
data: [ ... ],
length: 4 // 4 elements
}
This is obviously nonsense, but the point is that .length is a property of the array object, not a function of it, so it doesn't need trailing brackets.

Because its a known value on the object (a property) and not a function that needs to be evaluated.

In JavaScript, .length is a property and behaves as one:
var a = [1, 2, 3, 4, 5];
console.log(a.length); // returns 5
a.length = 3;
console.log(a.join(",")); // returns "1,2,3"
http://jsfiddle.net/GD89N/
Notice you can not only read, but also write the value of the property.
By the way, In C#, usually .Length or .Count are properties, not methods:
String.Length
Array.Length
List<T>.Count
Linq exposes a Count() method to IEnumerable<T>, but that's a relatively new addition.

There are many languages where properties can be backed by method calls including JavaScript. It is up to the implementors to decide how it will be implemented.
You can have your own properties backed by functions. I've already answered a similar question before, so won't repeat it here.
Ruby also does not require parentheses after a method call. This is valid Ruby code
[].size // size is an Array method
object.x = 42; // x= is a method (setter)
Python also also has the concept of function backed properties. Here's an example in Python:
class Foo(object):
def __init__(self):
self._x = None
#property
def x(self):
print "getter called"
return self._x
#x.setter
def x(self, value):
print "setter called"
self._x = value
f = Foo()
f.x = "20"
setter called
f.x
getter called
Parentheses is just syntax and has traditionally been used for function/method calls, but it's not a universal standard by any means.

Related

Why does JavaScript convert an array of one string to a string, when used as an object key? [duplicate]

This question already has answers here:
Why can I access object property with an array?
(2 answers)
Closed 2 years ago.
I ran into a scenario where JavaScript behaves in a way that is somewhat baffling to me.
Let's say we have an object with two keys foo & bar.
a = { foo: 1, bar: 2 }
Then, I have an array of strings, in this case one 'foo'
b = ['foo']
I would expect the following:
a[b] == undefined
a[b[0]] == 1
BUT, this is what happens:
a[b] == 1
a[b[0]] == 1
Why does JavaScript convert ['foo'] -> 'foo' when used as a key?
Does anyone out there know the reason?
How can this be prevented?
let a = { foo: 1, bar: 2 }
let b = ['foo']
console.log(a[b] == 1) // expected a[b] to be undefined
console.log(a[b[0]] == 1) // expected a[b] to be 1
All the object keys are string, so it eventually convert everything you place inside [] (Bracket notation) to string, if it's an expression it evaluates the expression and convert it's value to string and use as key
console.log(['foo'].toString())
Have a look at this example to understand, here [a] eventually converts a toString using a.toString() and then set it as key to b object
let a = { a : 1}
let b = {
[a] : a
}
// object converted to string
console.log(a.toString())
// object built using [] computed property access
console.log(b)
How can i stop this
In practical scenarios you should never do this, but just to illustrate, you can intercept or override the toString method of your object and return value as string with [] around:
let a = { foo: 1, bar: 2 }
let b = ['foo']
b.toString = function() {
let string = this.join(',')
return "[" + string + "]"
}
console.log(b.toString())
console.log(a[b])
When using an array as a key, javascript call the 'toString()' method of that array, and then try to find the stringified version of the array as the key. And if you call ['foo'].toString() you see this method returns "foo".
Why does JavaScript convert ['foo'] -> 'foo' when used as a key?
Does anyone out there know the reason?
Any time there is confusion as to why JavaScript acts in a way which may be unexpected, then looking at the language definition is the surefire way to exactly figure out what happened.
https://www.ecma-international.org/ecma-262/10.0/ is the most current language definition at the time of posting this.
First, you will want to find the area pertaining to Array access. It is in language lingo though.
12.3.2.1 Runtime Semantics: Evaluation
MemberExpression : MemberExpression [ Expression ]
...
3. Let propertyNameReference be the result of evaluating Expression.
4. Let propertyNameValue be ? GetValue(propertyNameReference).
6. Let propertyKey be ? ToPropertyKey(propertyNameValue).
So, what is happening here is you are accessing your array (the MemberExpression) using [] with an Expression.
In order to access with [] the Expression will be evaluated, and then GetValue will be called. Then ToPropertyKey will be called.
propertyNameReference = Evaluate Expression b = b
propertyNameValue = GetValue(propertyNameReference) = ['foo']
propertyKey = ToPropertyKey(propertyNameValue) = 'foo'
ToPropertyKey, in our situation, leads to ToPrimitive and then to ToOrdinaryPrimitive which states that we should call "toString" on the argument (['foo'] in our case).
This is where the implementation comes in to play. On the implementation side,
The Array object overrides the toString method of Object. For Array objects, the toString method joins the array and returns one string containing each array element separated by commas" MDN - Array toString
When there is only one value in the array, the result will simply be that value.
How can this be prevented?
This is the current way it is implemented. In order to change that, you must either change the default implementation, use detection to prevent the call, or use guidance to prevent the call.
Guidance
Document and enforce calling mechanisms in your code. This may not always be possible. It is at the very least reasonable to expect programmers to not call property access with arrays though.
Detection
This will depend on the current environment. With the most recent iteration of JavaScript, you can use type enforcement to ensure that property access is Number or String. Typescript makes this rather easy (here is a good example). It would essentially just require the access to be defined as:
function ArrayAccess(value: string | number) {
and this would prevent anyone from using the array as an accessor value.
Default Implementation
Changing the default implementation is a terrible idea. It will more than likely cause all sorts of breaking changes, and should not be done. However, just for completeness, here is what it would look like. Primarily I am showing this so you can hopefully recognize it if you see it somewhere and then kill it with fire (or check in some code to fix it if there were no spiders near it).
var arrayToString = [].toString;
Array.prototype.toString = function(){
if(this.length === 1) return;
return arrayToString.call(this);
};
Changing the instance implementation is not much of a better idea either. That is covered by #Code Maniac in a separate answer. "In practical scenarios you should never do this" #Code Maniac states, which I also agree with.
When using an array as a key, javascript call the 'toString()' method of that array, and then try to find the stringified version of the array as the key. And if you call ['foo'].toString() you see this method returns "foo".

Weird Array Objects - JavaScript

Arrays are quite something in JavaScript when compared with other programming languages and it's not without its full set of quirks.
Including this one:
// Making a normal array.
var normalArray = [];
normalArray.length = 0;
normalArray.push(1);
normalArray[1] = 2;
normalArray; // returns [1, 2]
normalArray.length // returns 2
So yes, the above is how we all know to make arrays and fill them with elements, right? (ignore the normalArray.length = 0 part for now)
But why is it that when the same sequence is applied on an object that's not purely an array, it looks a bit different and its length property is off by a bit?
// Making an object that inherits from the array prototype (i.e.: custom array)
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
customArray.length = 0;
customArray.push(1);
customArray[1] = 2;
customArray; // returns [1, 1: 2]
customArray.length // returns 1
Not entirely sure what's going on here but some explanation will be much appreciated.
This may not be the perfect answer, but according to my understanding of Javascript arrays, they are a little bit different than usual objects. (Mainly due to the fact that it maintains a length property, and Objects don't).
So if we take your code for an example:
var normalArray = [];
This is the right way to create an array in Javascript. But what about the below one?
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
Are they same? Let's see..
Array.isArray(normalArray); // true -> [object Array]
Array.isArray(customArray); // false -> [object Object]
So it is clear that although you inherit from the array prototype, it doesn't really create an object with Array type. It just creates a plain JS object, but with the inherited array functions. That's the reason why it updates the length when you set the value with customArray.push(1);.
But since your customArray is only a regular object and for a regular JS object, [] notation is used to set a property, it doesn't update the length (because Objects don't have a length property)
Hope it's clear :)
The array you are trying to create is not a pure array (as you are perhaps aware). Its basically a JavaScript object and is supposed to behave like an object.
While treating an object like an array, its up to you to maintain all it's array like features.
You specifically have to assign a length property to it and you did it correctly.
Next, the push method from Array.prototype is supposed to insert an element to the array and increment the length property (if any), so it did increment 0 to 1. There you go, the length now is 1.
Next you used the literal notation of property assignment to Object, which is similar to something like customArray['someProperty'] = 1.
While using literal notation, no method from Array.Prototype is being invoked and hence the customArray object never knows that it has to behave like an Array and its length property remains unaffected. It simply behaves like an object and you get what you got.
Remember the length is just a property on Array class and this property is appropriately incremented and decremented by every method on Array.
Note: Array like objects are not recommended and its up to you entirely to maintain the index and other Array stuff for such objects.
From what I can see, you have a problem with your function:
return this
This should be
return (this);
Just fixes any potential errors you might have. Another thing is you're not using the var keyword to declare customArray. These errors might be breaking your code.

JavaScript property access using brackets

If I declare the following in my Chrome console:
var object = {0:0, 1:1}
I can call object[0] and object[1] and get their values. I can also call object["0"] and object["1"]. Next, if I declare:
var object = {"0":0, "1":1}
I can also make all four of the above calls. But if I declare:
var object = {a:0, 1:1}
I get a ReferenceError of "a is not defined" when I call object[a], but object["a"] returns 0, even though the property name in the declaration is not a string. I guess JavaScript thinks I'm calling a variable that doesn't exist in the first example. But why do calling object[0] and object["0"] both work? It seems that JavaScript is doing some kind of automatic conversion for numbers (presumably since they can't be variable names), but what are the rules for this? And is this behavior universal to other places it might come up or just to the bracket notation for objects?
When you use brackets, the expression inside the brackets is evaluated. What's the value of the expression
a
?? Well, if "a" isn't a declared variable, it's nonsense. When you use . notation, the identifier (and it must be an identifier) following the operator is treated as a string. It's just the way the language works.
The reason you're getting a ReferenceError for object[a] is because a literal a is a variable in javascript. "a" is a string containing the letter a.
You can use the dot notation object.a or the bracket notation with object["a"]
object.a; //=> 0
object["a"]; //=> 0
object[1]; //=> 1
object["1"]; //=> 1
Or you can use a variable for access
var x = "a";
object[x]; //=> 0
var y = 1;
object[y]; //=> 1
You are correct.
a there is a token which the engine assumes is a variable.
If you type "a" JS knows it's a string-primitive.
If you type 0, JS knows it's a number-primitive.
So on top of obj.a, obj["a"], obj[0], obj["0"], you can also say:
var a = 0;
obj[a]; // 0
Your app is exploding, because a hasn't been defined yet, and now you want to use it.
And yes, this is the expected behaviour.
What's inside of the brackets isn't seen as a "part" of the object -- it's a way of saying "give me the value of the object which is referenced by this key", where the key might be a number or string (or something that can be coerced into a string or number).
In the future, with maps and weakmaps, you would actually be able to use other objects/functions as keys as well.
var obj = new Map(),
func = function () { },
el = document.getElementById("myId");
obj[func] = 1;
obj[el] = 2;
Right now, these things technically work... ...but only because they're converted to their string values... ...so if you had two functions which were written the same (but technically two different objects), you would overwrite values, currently.
Inside of a map, they'd be treated as separate objects.
Using DOM elements is even worse, right now, as it might be useful to store hundreds of those and bind references to them, so that you don't have to keep looking for them... ...but for now, you need to make a unique ID number/key for each one, and store that, and remember the keys, and then create a child object to hold the data you want...
Whereas in the future, with maps, you could say:
var my_els = document.querySelector(".lots-of-els");
for (let el of my_els /* also the future */) {
console.log( my_map_of_data[el].info );
}

What is the best way to check that multiple variables are set?

This is more of a general practices question.
The language I am working with is Javascript. I have a function that is getting an object with many variables (okay, just 10 variables). What is the best way to make sure this function is getting all the required variables and that they are all set?
I know, I know, why not just use a if statement. That's a big chunk of if statements! As I grow as a programmer, I know that may not be the best method for this. I'm looking for a shortcut actually. How would you check a large sum of variables for existence and non-blank values?
This is a pretty neat way of handling validation, I usually use this when checking for required fields in form inputs.
var theObj = { /* object loaded from server */ }
function checkTheObj(testObj)
{
var requiredKeys = ['key1', 'key2', 'key3'];
for(var keyPos = 0; keyPos < requiredKeys.length; keyPos++)
{
if(typeof(testObj[requiredKeys[keyPos]]) == 'undefined')
{
return false;
}
}
return true;
}
if(checkTheObj(theObj))
{
//do stuff
}
You can of course tweak this to return or throw an exception telling the first missing field (or use an internal array to return a list of all missing fields).
function objectHas(obj, properties) {
var len = properties.length
for (var i=0; i<len; i++) {
if (i in properties) {
if((!obj.hasOwnProperty(properties[i])) || (!obj.propertyIsEnumerable(properties[i]))) {
return false;
}
}
}
return true;
}
Usage:
if(objectHas(user, ["email", "password", "phone"])) {
console.log("awesome");
}
It's simple, but does the job.
Edit: On an ideal world you could extend the Object prototype for an even neater syntax such as if(object.has(["a", "b", "c"])), but apparently extending the Object prototype is the incarnation of evil so a function will have to do :)
First of all, you need to improve your understanding of these languages and learn the correct terminology.
There is no (single) language named "Javascript" at all. You are implicitly using several languages here (depending on the runtime environment), all of which are ECMAScript implementations, and one of which is Netscape/Mozilla JavaScript (in Mozilla-based software like Firefox).
An object does not have variables, it has properties (not: keys). Global code, function code, and eval code can have variables; that is a different (but similar) concept.
The function is not getting an object, it is being passed a reference to an object as argument.
As a programmer, you should already know that you can do repetitive tasks in a loop; the associated statements in ECMAScript implementations are for, for-in, while and do. So you do not have to write several if statements.
You can access the properties of an object in two ways, where property is the property name:
Dot notation: obj.property
Bracket notation: obj["property"]
The second one is equivalent to the first if the property name is an identifier, i.e. if it follows certain naming rules. If the property name is not an identifier or if it is variable, you have to use the second one. This also shows that all property names are string values. So you can store the name of a property as value of a variable or another property, and then access the variable or property in the property accessor. In the following, a property name (property) is stored in and used from a variable:
var propertyName = "property";
obj[propertyName]
Combining that with a loop allows you to iterate over certain properties of an object. Unfortunately, the solutions presented so far are flawed in two respects: A for-in statement iterates only over the enumerable properties of an object, and it does so in arbitrary order. In addition, it also iterates over the enumerable inherited properties (which is why one solution requires the hasOwnProperty() call).
A simple, sure and efficient way to iterate only over certain properties of an object in a defined order looks as follows:
var propertyNames = ['name1', 'name2', 'name3'];
for (var i = 0, len = propertyNames.length; i < len; ++i)
{
/* … */ myObject[propertyNames[i]] /* … */
}
This works because propertyNames refers to an Array instance, which encapsulates an array data structure. The elements of an array are the properties of the Array instance that have integer indexes from 0 to 65535 (232−1). Because indexes are not identifiers (they start with a decimal digit), you have to use the bracket property accessor syntax (some people misunderstand this and refer to all ECMAScript objects as "arrays", even call them "associative arrays" and […] the "Array operator"). Therefore, propertyNames[i] evaluates to the values of the elements of the array in each iteration as i is increased by 1 each time. As a result, myObject[propertyNames[i]] accesses the property with that name in each loop.
Now, to find out whether the property is set, you need to define what that means. Accessing a property that does not exist results in the undefined value (not in an error). However an existing property may also have the undefined value as its value.
If "not set" means that the object does not have the property (but may inherit it), then you should use hasOwnProperty() as used in Mahn's solution.
If "not set" means that the object does not have the property and does not inherit it, then you should use the in operator, provided that the object is not a host object (because the in operator is not specified for them):
if (propertyNames[i] in obj)
If "not set" means that the object either has or inherits the property, but the property has the undefined value, or the object neither has nor inherits the property, then you should use the typeof operator as used in Bob Davies' and aetaur's solutions (but the latter approach using Array.prototype.every() is less compatible as-is; that method was not specified before ECMAScript Edition 5, and is not available in IE/JScript < 9).
There is a third option with ECMAScript Edition 5.x, the Object.keys() method which (despite its name) returns a reference to an Array instance that holds the names of all not-inherited properties of the argument:
var propertyNames = Object.keys(obj);
/* continue as described above */
It is a good idea to emulate Object.keys() if it is not built-in, as this algorithm is frequently useful.
This expression returns true, if all variables from variableNameList (list of required variable names) set in object o:
variableNameList.every(function(varName){ return typeof o[varName] !== 'undefined'; });
You can use underscore _.all function instead of native every, and underscore _.isUndefined instead of typeof ....

JavaScript: Use Object As Array

Are there any pitfalls to code like this?
var Foo = function() {
this.bar = function() { return 'bar'; };
};
var f = new Foo();
f[0] = 'hi';
f[1] = 'there';
Note that I'm creating a new function object with some misc properties, and then I'm treating the object like an array. Also how are the array values being stored in the object? Are 0 and 1 treated like property names?
Well, yes, 0, and 1 will be just two property names.
When you assign a property with the bracket notation, the expression between the brackets will be converted to String, and that string will be used as the property name.
In fact, even the indexes for real arrays are just that, properties:
var realArray = ['a'];
realArray.hasOwnProperty('0'); // true
The difference is that real array objects on every property assignment that correspond to a valid index[1], track internally the value of their length property.
That's one of the reasons why "subclassing" array objects is difficult, even with the new ECMAScript 5 extensions, or at the moment also with the proposed ECMAScript-Harmony Proxies 2 3, can't be completely done, in a stanard way.
That can be a problem, depending on how you plan to iterate the numeric properties.
If you enumerate the properties with the for-in statement, other members will appear, not just the numeric properties.
[1] A valid array index is any unsigned 32-bit integer in the range of 0 to (2^32)-1.
I think you might have problems if you try to loop through that with a for in loop; the loop will also get bar. There are ways around this, so you just have to be careful.
If you want to extend Array behavious please use : Array.prototype.yourfunc = function()....

Categories