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

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

Related

How can I mitigate injection/exfiltration attacks from dynamic property accesses (i.e. square bracket notation) in JavaScript?

After setting up eslint-plugin-security, I went on to attempt to address nearly 400 uses of square brackets in our JavaScript codebase (flagged by the rule security/detect-object-injection). Although this plugin could be a lot more intelligent, any uses of square brackets could possibly be an opportunity for a malicious agent to inject their own code.
To understand how, and to understand the whole context of my question, you need to read this documentation: https://github.com/nodesecurity/eslint-plugin-security/blob/master/docs/the-dangers-of-square-bracket-notation.md
I generally tried to use Object.prototype.hasOwnProperty.call(someObject, someProperty) where I could to mitigate the chance that someProperty is maliciously set to constructor. Lot of situations were simply dereferencing an array index in for loops (for (let i=0;i<arr.length;i++) { arr[i] }) If i is always a number, this is obviously always safe.
One situation I don't think I have handled perfectly, are square bracket assignments like this:
someObject[somePropertyPotentiallyDefinedFromBackend] = someStringPotentiallyMaliciouslyDefinedString
I think the easiest way to solve this issue is with a simple util, safeKey defined as such:
// use window.safeKey = for easy tinkering in the console.
const safeKey = (() => {
// Safely allocate plainObject's inside iife
// Since this function may get called very frequently -
// I think it's important to have plainObject's
// statically defined
const obj = {};
const arr = [];
// ...if for some reason you ever use square brackets on these types...
// const fun = function() {}
// const bol = true;
// const num = 0;
// const str = '';
return key => {
// eslint-disable-next-line security/detect-object-injection
if (obj[key] !== undefined || arr[key] !== undefined
// ||
// fun[key] !== undefined ||
// bol[key] !== undefined ||
// num[key] !== undefined ||
// str[key] !== undefined
) {
return 'SAFE_'+key;
} else {
return key;
}
};
})();
You'd then use it like so:
someObject[safeKey(somePropertyPotentiallyDefinedFromBackend)] = someStringPotentiallyMaliciouslyDefinedString
This means if the backend incidentally sends JSON with a key somewhere of constructor we don't choke on it, and instead just use the key SAFE_constructor (lol). Also applies for any other pre-defined method/property, so now the backend doesn't have to worry about JSON keys colliding with natively defined JS properties/methods.
This utility function is nothing without a series of passing unit tests. As I've commented not all the tests are passing. I'm not sure which object(s) natively define toJSON - and this means it may need to be part of a hardcoded list of method/property names that have to be blacklisted. But I'm not sure how to find out every one of these property methods that needs to be blacklisted. So we need to know the best way anyone can generate this list, and keep it updated.
I did find that using Object.freeze(Object.prototype) helps, but I don't think methods like toJSON exist on the prototype.
How can we make sure the property being set is essentially not already defined on vanilla objects? (i.e. constructor)
It is more important to prevent a key from being accessed on the wrong object than to validate/protect object keys themselves. Designating certain object keys as ‘unsafe’ and avoiding accessing just these regardless of the circumstances is just another form of the ‘sanitizing’ anti-pattern. If the object doesn’t contain sensitive data in the first place, there is no risk of it being exfiltrated or modified by untrusted inputs. You don’t need to worry about accessing src or innerHTML if you don’t access it on a DOM node; you don’t need to worry about exposing eval if you don’t perform lookups on the global object. As such:
Only use bracket notation with objects that either are arrays or specifically contain nothing other than a mapping from arbitrary strings to other values (those usually constructed by object literal notation); this kind of object I’m going to call map-like below. When using map-like objects, also ensure the following:
that you never store functions (or, in later versions of ECMAScript, classes or proxies to either) directly in a map-like object. This is to avoid problems when keys like 'toJSON' or 'then' map to functions that the language may then interpret as methods that modify the behaviour of the object. If, for some reason, you need to store functions in a map-like object, put the function in a wrapper like { _: function () { /* ... */ } }.
that you never coerce map-like objects to strings using built-in language mechanisms: the toString method, the String constructor (with or without new), the + operator or Array.prototype.join. This is to avoid triggering problems when the 'toString' key is set on a map-like object, as even a non-function value will prevent the default coercion behaviour from occurring and will instead throw a TypeError.
When accessing arrays, make sure the index is indeed an integer. Consider also using built-in methods like push, forEach, map or filter that avoid explicit indexing altogether; this will reduce the number of places you will need to audit.
If you ever need to associate arbitrary data with an object with a relatively fixed set of keys, e.g. a DOM node, window or an object you defined with class (all of which I’ll call class-like below), and for some reason WeakMap is not available, put the data it on a hardcoded key; if you have more than one such data item, put it in a map-like object a stored on a hardcoded key.
Even when following the above though, you may still fall victim to an injection or exfiltration attack by inadvertently accessing a property of Object.prototype. Particularly worrying are constructor and various built-in methods (which can be leveraged to access the Function object, and ultimately perform arbitrary code execution), and __proto__ (which can be used to modify the prototype chain of an object). To protect against those threats, you may try some of the following strategies. They are not mutually exclusive, but for the sake of consistency it may be preferable to stick with just one.
Mangle all keys: this is probably the (conceptually) simplest option, portable even to engines from the days of ECMAScript 3, and robust even against future additions to Object.prototype (as unlikely as they are). Simply prepend a single non-identifier character to all keys in map-like objects; this will safely namespace away untrusted keys from all reasonably conceivable JavaScript built-ins (which presumably should have names that are valid identifiers). When accessing map-like objects, check for this character and strip it as appropriate. Following this strategy will even make concerns about methods like toJSON or toString mostly irrelevant.
// replacement for (key in maplike)
function maplikeContains(maplike, key) {
return ('.' + key) in maplike;
}
// replacement for (maplike[key])
function maplikeGet(maplike, key) {
return maplike['.' + key];
}
// replacement for (maplike[key] = value)
function maplikeSet(maplike, key, value) {
return maplike['.' + key] = value;
}
// replacement for the for-in loop
function maplikeEach(maplike, visit) {
for (var key in maplike) {
if (key.charAt(0) !== '.')
continue;
if (visit(key.substr(1), maplike[key]))
break;
}
}
Mangling all keys indiscriminately ensures that you will not end up confusing unmangled keys for mangled keys or vice versa. For example if, like in the question, you mangle 'constructor' into 'SAFE_constructor', but leave 'SAFE_constructor' itself as-is, then after mangling both keys will end up referring to the same data, which may be a security vulnerability in itself.
A drawback of this approach is that the prefix character is going to end up in JSON, if you ever serialise such a map-like object.
Enforce direct property access. Read accesses can be guarded with Object.prototype.hasOwnProperty, which will stop exfiltration vulnerabilities, but will not protect you from inadvertently writing to __proto__. If you never mutate such a map-like object, this should not be a problem. You can even enforce immutability using Object.seal. If don’t want that though, you may perform property writes via Object.defineProperty, available since ECMAScript 5, which can create properties directly on the object, bypassing getters and setters.
// replacement for (key in maplike)
function maplikeContains(maplike, key) {
return Object.prototype.hasOwnProperty.call(maplike, key);
}
// replacement for (maplike[key])
function maplikeGet(maplike, key) {
if (Object.prototype.hasOwnProperty.call(maplike, key))
return maplike[key];
}
// replacement for (maplike[key] = value)
function maplikeSet(maplike, key, value) {
Object.defineProperty(maplike, key, {
value: value,
writable: true,
enumerable: true,
configurable: true
});
return value;
}
// replacement for the for-in loop
function maplikeEach(maplike, visit) {
for (var key in maplike) {
if (!Object.prototype.hasOwnProperty.call(maplike, key))
continue;
if (visit(key, maplike[key]))
break;
}
}
Clear the prototype chain: make sure map-like objects have an empty prototype chain. Create them via Object.create(null) (available since ECMAScript 5) instead of {}. If you created them via direct object literals before, you can wrap them in Object.assign(Object.create(null), { /* ... */ }) (Object.assign is available since ECMAScript 6, but easily shimmable to earlier versions). If you follow this approach, you can use the bracket notation as usual; the only code you need to check is where you construct the map-like object.
Objects created by JSON.parse will by default will still inherit from Object.prototype (although modern engines at least add a JSON key like __proto__ directly on the constructed object itself, bypassing the setter from the prototype’s descriptor). You can either treat such objects as read-only, and guard read accesses by hasOwnProperty (as above), or strip away their prototypes by writing a reviver function that calls Object.setPrototypeOf. A reviver function can also use Object.seal to make an object immutable.
function maplikeNew(maplike) {
return Object.assign(Object.create(null), maplike);
}
function jsonParse(json) {
return JSON.parse(json, function (key, value) {
if (typeof value === 'object' && value !== null && !Array.isArray(value))
Object.setPrototypeOf(value, null);
return value;
});
}
Use Maps instead of map-like objects: using Map (available since ECMAScript 6) allows you to use keys other than strings, which isn’t possible with plain objects; but even just with string keys you have the benefit that entries of the map are completely isolated from the prototype chain of the map object itself. Items in Maps are accessed by .get and .set methods instead of the bracket notation and cannot clash with properties at all: the keys exist in a separate namespace.
There is however the problem that a Map cannot be directly serialized into JSON. You can remedy this by writing a replacer function for JSON.stringify that converts Maps into plain, prototype-free map-like objects, and a reviver function for JSON.parse that turns plain objects back into Maps. Then again, naïvely reviving every JSON object into a Map is also going to cover structures I called ‘class-like’ above, which you probably don’t want. To distinguish between them, you might need to add some kind of schema parameter to your JSON-parsing function.
function jsonParse(json) {
return JSON.parse(json, function (key, value) {
if (typeof value === 'object' && value !== null && !Array.isArray(value))
return new Map(Object.entries(value));
return value;
});
}
function jsonStringify(value) {
return JSON.stringify(value, function (key, value) {
if (value instanceof Map)
return Object.fromEntries(value.entries());
return value;
});
}
If you ask me for my preference: use Map if you don’t need to worry about pre-ES6 engines or JSON serialisation; otherwise use Object.create(null); and if you need to work with legacy JS engines where neither is possible, mangle keys (first option) and hope for the best.
Now, can all this discipline be enforced mechanically? Yes, and it’s called static typing. With good enough type definitions, TypeScript should be able to catch cases where class-like objects are accessed in map-like fashion and vice versa. It can even catch some cases where an object with an unwanted prototype appears:
type Maplike<T> = {
[K: string]: T|undefined;
constructor?: T;
propertyIsEnumerable?: T;
hasOwnProperty?: T;
toString?: T;
toLocaleString?: T;
valueOf?: T;
};
const plain: { [K: string]: string } = {};
const naked: Maplike<string> = Object.create(null); // OK
const error: Maplike<string> = {}; // type error
function f(k: string) {
naked[k] = 'yay'; // OK
plain[k] = 'yay'; // OK
document.body[k] = 'yay'; // type error
}
console.info(plain.toString()); // OK
console.info(naked.toString()); // type error
Bear in mind this is no panacea, however. The above type definition may catch the most obvious mistakes, but it’s not too hard to come up with a case which it will not detect.

Subclassing built-in constructors and internal properties in Javascript

http://speakingjs.com/es5/ch28.html#_obstacle_1_instances_with_internal_properties
Most built-in constructors have instances with so-called internal
properties (see Kinds of Properties), whose names are written in
double square brackets, like this: [[PrimitiveValue]]. Internal
properties are managed by the JavaScript engine and usually not
directly accessible in JavaScript.
Furthermore, adding internal properties to an existing instance (1) is
in general impossible, because they tend to fundamentally change the
instance’s nature.
The work around proposed in order to add internal properties. http://speakingjs.com/es5/ch28.html#_workaround_for_obstacle_1
function MyArray(/*arguments*/) {
var arr = [];
// Don’t use Array constructor to set up elements (doesn’t always work)
Array.prototype.push.apply(arr, arguments); // (1)
copyOwnPropertiesFrom(arr, MyArray.methods);
return arr;
}
MyArray.methods = {
get size() {
var size = 0;
for (var i=0; i < this.length; i++) {
if (i in this) size++;
}
return size;
}
}
function copyOwnPropertiesFrom(target, source) {
Object.getOwnPropertyNames(source) // (1)
.forEach(function(propKey) { // (2)
var desc = Object.getOwnPropertyDescriptor(source, propKey); // (3)
Object.defineProperty(target, propKey, desc); // (4)
});
return target;
};
Interaction
> > var a = new MyArray('a', 'b')
> a.length = 4;
> a.length
4
> a.size
2
1º I understand, by adding only a getter to the property size, there's no way to set a.size
2º I also understand that we are defining a new property in an array by MyArray.methods, but I don't see in which kind of way it's been added "a new internal property". As far as I am concerned, internal properties are a kind of property not directly accessible by Javascript.
Could "size" be considered an internal property itself?
Greetings
I don't see in which kind of way it's been added "a new internal property". As far as I am concerned, internal properties are a kind of property not directly accessible by Javascript.
Yes, the point of an internal property is that you can't manually create it. So instead of constructing our object and adding the internal property that we want, we need to use an object which already has that internal property, and copy our methods onto it - basically as a mixin, because we cannot use prototypical inheritance.
The one we're after is the internal instance method [[DefineOwnProperty]] of arrays which treats integer properties specially, and we also want to have the special behaviour of the .length property (that is not internal, but cannot be manually constructed either). So we use var arr = [] to get these, and put our size custom property on it.
Could "size" be considered an internal property itself?
No. "internal" means that it cannot be accessed via the means of the language, and are only used to describe the behaviour in the specification.

What is the best way to prototype Array Class with a new Methods

I have many functional methods i need to use it and i need to publish a library with this methods to share it with JavaScript developers it helps very much so for instance i need to add a Method named duplicates will return to me the duplicates of the Array
as you can see this method is not officially published by ECMA so i dont know the best form to put the script
1-
Array.prototype.duplicate = function (){
//script here its usefull to use `this` refer to the Array
}
Using it like
[1,2,2].duplicates();
2-
var Ary = function(a){
if(!(this instanceOf Ary))
return new Ary(a)
if(Object.prototype.toString.call(a) != '[object Array]')
return new Error(a + 'is not an Array')
else
{
for(var i =0 ; i<a.length; i++)
{
this.push(a[i]);
}
}
}
Ary.prototype = new Array();
Ary.prototype.constructor = Ary;
Ary.prototype.duplicates = function(){
//script here its usefull to use `this` refer to the Array
};
Using it like
Ary([1,2,2]).duplicates();
i need to know is it more like it to use prototype directly to Array JavaScript Class to add functionality if it is not officialy published with ECMA and instead we do inherit from Array Class and then play with it ???
or its ok do prototype it ??
and whats the consequences
Regards
For your own code, it's fine to add a duplicates method to Array.prototype but you do need to be prepared for what may happen if you use code (either your own, or something you're using) that incorrectly uses for..in to loop through arrays like this:
for (var i in myArray) { // <==== Wrong without safeguards
}
...because i will get the value "duplicates" at some point, since for..in loops through the enumerable properties of an object and its prototype(s), it does not loop through array indexes. It's fine to use for..in on arrays if you handle it correctly, more in this other answer on SO.
If you're only going to work in an ES5-enabled environment (modern browsers, not IE8 and earlier), you can avoid that by adding your duplicates via Object.defineProperty, like this:
Object.defineProperty(Array.prototype, "duplicates", {
value: function() {
// ...the code for 'duplicates' here
}
});
A property defined that way is not enumerable, and so does not show up in for..in loops, so code that fails to correctly handle for..in on arrays isn't impacted.
Unfortunately, it's currenty impossible in JavaScript to correctly derive from Array.prototype (your second option), because Array has special handling of properties whose names are all digits (called "array indexes") and a special length property. Neither of these can currently be correctly provided in a derived object. More about those special properties in my blog article A Myth of Arrays.
As a general rule: don't monkey patch the native Javascript object prototypes. It may appear harmless, but if you're including third party code in your site/application, it can cause all kinds of subtle bugs.
Modifying Array prototype is particularly evil, because the internet is rife with buggy, incorrect code that iterates arrays using the for ... in construct.
Check it out:
for(var i in [1,2,3]) {
console.log(i);
}
Outputs:
1
2
3
But if you've modified the Array prototype as follows:
Array.prototype.duplicates = function() { }
It outputs
1
2
3
duplicates
See for yourself.

Can an Object object be coerced into an Array object?

Crockford writes in http://javascript.crockford.com/survey.html:
"There are two ways to make a new array:
var myArray = [];
var myArray = new Array();"
So I'm confused by these two lines in some AJAX code I am reading:
var obj={}; // obj is an Object object (i.e. a hash table)
obj[4] = 'x'; // now obj is suddenly an Array object via an integer key?
In JavaScript are an object and an array really just the same thing, but with a variant on the key type?
In other words, is this the same as in php where we can use either a name (string) or an integer for a hash key?
I've Googled for an answer on this but can't seem to nail down an article which discusses this issue.
One possibility that comes to mind is that perhaps the first line is syntactic lint because the 2nd line overwrites the previous definition of obj as it creates a new Array object.
it does not become an array, it is simply an Object with a '4' property, like this:
var obj = {
'4': 'x'
};
it is just converted to a string when used as a property like obj['4'] = 'x';
Everything but primitive datatypes is an object in JavaScript. Objects can have a properties and there are two ways to access object properties:
Dot notation, foo.bar, which you can use as long as the property name is a valid identifier.
Bracket notation, foo['bar'] which you have to use if the key is not a valid identifier [spec]. For example, if it is a number, or contains a space or you have a variable with the name.
Hence, bracket notation is not a characteristic of arrays and if you see it, it does not mean the value is an array. It is simple one of two ways of accessing properties.
The elements of an array are just properties with numeric keys. Arrays are built on top of objects and implement some additional methods which treat these numeric properties in a special way. For example the .length property is automatically updated when you add new elements. But ultimately they are just normal properties.
In your example you have a simple object. You have to access the property with obj[4] or obj['4'] because obj.4 is invalid since 4 is not a valid identifier (basically everything that you can use as variable name is a valid identifier. var 4 = 'foo'; is invalid).
And since arrays are just objects, if you could use numbers as identifiers, you were also able to access an element with arr.4.
As far as I know, no, an object can't be coerced into an array. But, it can look and act like an array, and that's what's happening here. Numbers, and anything else that can be coerced to a string, are perfectly valid property names for Javascript objects, so
obj[4] = 1;
obj['spam'] = 2;
are both valid ways of setting a property on the object. That doesn't make the object an array. An Array is a special class of object with specific methods (.slice(), .concat(), etc) and a length property that's kept up to date with the number of items in the array.
Yes
Javascript Array is very different from tradition array, you can think of it as object.
var array = [1,2,3] is equivalent to var object = {'0' : 1, '1' : 2, '2' : 3}
except array inherited from Array.prototype and object inherited from Object.prototype, where Array.prototype will contain method such as length.
Javascript is a loosely-typed, prototype-based language. Even primitive types like a boolean can be treated like an object (though you aren't going to get far). Almost everything in javascript is, at root, an object.
Understanding this, an array IS an object. You can arbitrarily add properties to any object:
var xml = new XMLHttpRequest();
xml[4] = 'x';
console.log(xml);
That object is still an instance of XMLHttpRequest. It now has a property labeled 4 with a value of x. You can treat anything like this -- even a function:
var test_func = function () {
alert('woah!');
}
test_func[4] = 'x';
console.log(test_func[4]);
The take-away here is that the obj[key] = value notation is NOT indicative of an "array" type, like it is in languages such as PHP. Rather, it is an alternate way to access properties of any object, and is equivalent to obj.key = value (you can't use obj.4 = 'x', though, that's invalid syntax). The other take-away is that any object in javascript can be modified or used in pretty much any way. You shouldn't misuse objects, but you can
Check it out here: http://jsfiddle.net/w2AqJ/
Documentation
Array on MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
Javascript "associative arrays" considered harmful by Andrew Dupont - http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/

JavaScript Loops: for...in vs for

I faced a strange behaviour in Javascript. I get
"Object doesn't support this property or method"
exception for the removeAttribute function in the following code:
var buttons = controlDiv.getElementsByTagName("button");
for ( var button in buttons )
button.removeAttribute('disabled');
When I change the code with the following, the problem disappears:
var buttons = controlDiv.getElementsByTagName("button");
for ( var i = 0; i < buttons.length; i++ )
buttons[i].removeAttribute('disabled');
What is the value of button inside the for...in?
Don't use for..in for Array iteration.
It's important to understand that Javascript Array's square bracket syntax ([]) for accessing indicies is actually inherited from the Object...
obj.prop === obj['prop'] // true
The for..in structure does not work like a more traditional for..each/in that would be found in other languages (php, python, etc...).
Javascript's for..in is designed to iterate over the properties of an object. Producing the key of each property. Using this key combined with the Object's bracket syntax, you can easily access the values you are after.
var obj = {
foo: "bar",
fizz: "buzz",
moo: "muck"
};
for ( var prop in obj ) {
console.log(prop); // foo / fizz / moo
console.log(obj[prop]); // bar / buzz / muck
}
And because the Array is simply an Object with sequential numeric property names (indexes) the for..in works in a similar way, producing the numeric indicies just as it produces the property names above.
An important characteristic of the for..in structure is that it continues to search for enumerable properties up the prototype chain. It will also iterate inherited enumerable properties. It is up to you to verify that the current property exists directly on the local object and not the prototype it is attached to with hasOwnProperty()...
for ( var prop in obj ) {
if ( obj.hasOwnProperty(prop) ) {
// prop is actually obj's property (not inherited)
}
}
(More on Prototypal Inheritance)
The problem with using the for..in structure on the Array type is that there is no garauntee as to what order the properties are produced... and generally speaking that is a farily important feature in processing an array.
Another problem is that it usually slower than a standard for implementation.
Bottom Line
Using a for...in to iterate arrays is like using the butt of a screw driver to drive a nail... why wouldn't you just use a hammer (for)?
for...in is to be used when you want to loop over the properties of an object. But it works the same as a normal for loop: The loop variable contains the current "index", meaning the property of the object and not the value.
To iterate over arrays, you should use a normal for loop. buttons is not an array but a NodeList (an array like structure).
If iterate over buttons with for...in with:
for(var i in a) {
console.log(i)
}
You will see that it output something like:
1
2
...
length
item
because length and item are two properties of an object of type NodeList. So if you'd naively use for..in, you would try to access buttons['length'].removeAttribute() which will throw an error as buttons['length'] is a function and not a DOM element.
So the correct way is to use a normal for loop. But there is another issue:
NodeLists are live, meaning whenever you access e.g. length, the list is updated (the elements are searched again). Therefore you should avoid unnecessary calls to length.
Example:
for(var i = 0, l = buttons.length; i < l, i++)
for(var key in obj) { } iterates over all elements in the object, including those of its prototypes.
So if you are using it and cannot know nothing extended Object.prototype you should always test obj.hasOwnProperty(key) and skip the key if this check returns false.
for(start; continuation; loop) is a C-style loop: start is executed before the loop, continuation is tested and the loop only continues while it's true, loop is executed after every loop.
While for..in should not generally be used for Arrays, however prior to ES5 there was a case for using it with sparse arrays.
As noted in other answers, the primary issues with for..in and Arrays are:
The properties are not necessarily returned in order (i.e. not 0, 1, 2 etc.)
All enumerable properties are returned, including the non–index properties and those on the [[Prototype]] chain. This leads to lower performance as a hasOwnProperty test is probably required to avoid inherited properties.
One reason to use for..in prior to ES5 was to improve performance with sparse arrays, provided order doesn't matter. For example, in the following:
var a = [0];
a[1000] = 1;
Iterating over a using for..in will be much faster than using a for loop, as it will only visit two properties whereas a for loop will try 1001.
However, this case is made redundant by ES5's forEach, which only visits members that exist, so:
a.forEach();
will also only iterate over two properties, in order.

Categories