Note: Arrays in JavaScript are not associative arrays. There are only objects in JavaScript to manage key-value correspondence. But associative arrays maintain order, while objects do not.
Since the for in loop enumerates all properties on the prototype chain, the only way to filter these properties is to use the `hasOwnProperty` function, so it will be many times slower than an ordinary for loop.
Iteration
In order to achieve the best performance of traversing the array, it is recommended to use the classic for loop.
var list = [1, 2, 3, 4, 5, ... 100000000];
for(var i = 0, l = list.length; i < l; i ) {
console.log(list[i]);
}
The above code has a processing, which is to cache the length of the array through l = list.length.
Although length is a property of the array, there is a performance overhead in accessing it in each loop. Maybe the latest JavaScript engines have optimized this, but we can't guarantee whether our code will run on these latest engines.
In fact, the way without caching the array length is much slower than the cached version.
`length` property (The `length` property)
The getter method of the length property will simply return the length of the array, while the setter method will truncate the array.
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo; // [1, 2, 3]
Translator's Note: Viewing the value of foo in Firebug at this time is: [1, 2, 3, undefined, undefined, undefined] But this result is not accurate, if you Check the result of foo in Chrome's console. You will find that it is like this: [1, 2, 3] Because undefined is a variable in JavaScript. Note that the variable is not a keyword, so the meaning of the above two results is completely different. Same.
// Translator’s Note: To verify, let’s execute the following code to see if the serial number 5 exists in foo.
5 in foo; // Returns false whether in Firebug or Chrome
foo[5] = undefined;
5 in foo; // Returns true whether in Firebug or Chrome
is set to length A smaller value will truncate the array, but increasing the length property has no effect on the array.
In conclusion (In conclusion)
For better performance, it is recommended to use a normal for loop and cache the length property of the array. Using for in to iterate over an array is considered bad coding practice and tends to generate errors and cause performance issues.
Array Constructor
Since Array's constructor is a bit ambiguous in how it handles its arguments, it is always recommended to use the literal syntax for arrays - [] - to create arrays.
[1, 2, 3]; // Result: [1, 2, 3]
new Array(1, 2, 3); // Result: [1, 2, 3]
[3]; // Result: [3]
new Array(3); // Result: []
new Array('3') // Result: ['3']
Translator's Note: The ambiguity here refers to the two constructor syntaxes of arrays var arr1 = new Array(arrayLength); var arr2 = new Array(element0, element1, ..., elementN);
// Translator’s Note: So the following code will be very confusing
new Array(3, 4, 5); // Result: [3, 4, 5]
new Array(3) // Result: [], the length of this array is 3
Since only one parameter is passed to the constructor (Translator's Note: Refers to the calling method of new Array(3);), and this parameter is a number, The constructor returns an empty array with the length property set to this parameter. It is important to note that only the length property is set at this time, and the real array is not generated. Translator's Note: In Firebug, you will see [undefined, undefined, undefined], which is actually wrong. There is a detailed analysis in the previous section.
var arr = new Array(3);
arr[1]; // undefined
1 in arr; // false, the array has not been generated yet
This takes precedence over setting the array The length attribute is only useful in a few situations, such as when you need to loop a string to avoid the trouble of a for loop.
new Array(count 1).join(stringToRepeat);
// Translator's Note: new Array(3).join('#') will return "##"
Conclusion (In conclusion)
You should try to avoid using the array constructor to create a new array. It is recommended to use the literal syntax of arrays. They are shorter and more concise, thus increasing the readability of the code.
for in loop
Like the in operator, the for in loop also traverses all properties on the prototype chain when looking for object properties.
Note: The for in loop will not iterate over properties whose enumerable is set to false; such as the length property of an array.
// Modify Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // Output two attributes: bar and moo
}
Since it is impossible to change the behavior of for in itself, it is necessary to filter out those that are not expected Properties that appear in the loop body, this can be done through the `hasOwnProperty` function on the Object.prototype prototype.
Note: Since for in always traverses the entire prototype chain, performance will be affected if the inheritance level of an object is too deep.
Using `hasOwnProperty` for filtering
// The foo variable is the one in the above example
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
This version of the code is the only correct way to write it. Since we used hasOwnProperty, only moo is output this time. If hasOwnProperty is not used, this code may break when native object prototypes (such as Object.prototype) are extended.
A widely used class library, Prototype, extends native JavaScript objects. Therefore, when this class library is included in the page, the for in loop without hasOwnProperty filtering will inevitably cause problems.
Best practices
It is recommended to always use hasOwnProperty. Don't make any assumptions about the environment in which your code is running, and don't assume whether native objects have been extended.
typeof operator
The typeof operator (along with `instanceof`) is perhaps the biggest design flaw in JavaScript because it is almost impossible to get the desired results from them.
Although instanceof has some very few application scenarios, typeof has only one practical application (Translator's Note: This practical application is used to detect whether an object has been defined or has been assigned a value), and this application has Not used to check the type of an object.
Note: Because typeof can also be called with function syntax, such as typeof(obj), but this is not a function call. Those two parentheses are only used to calculate the value of an expression, and the return value will be used as an operand of the typeof operator. There is actually no function named typeof.
The JavaScript type table Value Class Type
-------------------------- ------------------
"foo" String string
new String("foo") String object
1.2 Number number
new Number(1.2 ) Number object
true Boolean boolean
new Boolean(true) Boolean object
new Date() Date object
new Error() Error object
[1,2,3] Array object
new Array(1, 2, 3) Array object
new Function("") Function function
/abc/g RegExp object (function in Nitro/V8)
new RegExp("meow") RegExp object (function in Nitro/V8)
{} Object object
new Object() Object object
In the above table, the Type column represents the operation result of the typeof operator. As you can see, this value returns "object" in most cases.
Class A column representing the value of the object’s internal property [[Class]].
JavaScript standard document defines: [[Class]] The value can only be one of the following strings: Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp , String.
In order to get the [[Class]] of the object, we need to use the method toString defined on Object.prototype.
The Class of an object (The Class of an object)
The JavaScript standard document only gives one way to obtain the [[Class]] value, which is to use Object.prototype.toString.
function is(type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
}
is('String', 'test'); // true
is('String', new String('test')); // true
In the above example , the Object.prototype.toString method is called, and this is set to the object whose [[Class]] value needs to be obtained.
Translator's Note: Object.prototype.toString returns a standard format string, so the above example can intercept the string at the specified position through slice, as shown below:
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
ES5 Tip: In ECMAScript 5, for convenience, the Object.prototype.toString method is called on null and undefined, and its return value is changed from Object to Null and Undefined.
Translator's Note: This change can be seen in IE8 and Firefox 4, as shown below:
// IE8
Object.prototype.toString.call(null) // "[object Object]"
Object.prototype.toString.call (undefined) // "[object Object]"
// Firefox 4
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString .call(undefined) // "[object Undefined]"
Testing for undefined variables
typeof foo !== 'undefined'
The above code will detect Whether foo has been defined; if it is not defined and used directly, a ReferenceError exception will occur. This is the only place typeof is useful.
In conclusion
In order to detect the type of an object, it is highly recommended to use the Object.prototype.toString method; because this is the only reliable way. As shown in the table above, some return values of typeof are not defined in the standard document, so different engine implementations may differ.
We should try to avoid using the typeof operator unless we want to check whether a variable has been defined.
instanceof operator
instanceof operator is used to compare the constructor of two operands. Only meaningful when comparing custom objects. If used to compare built-in types, it would be as useless as the typeof operator.
Comparing custom objects
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
// If you only set Bar.prototype to the function Foo itself, rather than an instance of the Foo constructor
Bar.prototype = Foo;
new Bar () instanceof Foo; // false
`instanceof` Using `instanceof` with native types)
new String('foo') instanceof String; // true
new String('foo ') instanceof Object; // true
'foo' instanceof String; // false
'foo' instanceof Object; // false
One thing to note, instanceof An error will occur when comparing objects belonging to different JavaScript contexts (for example, different document structures in the browser) because their constructors will not be the same object.
In conclusion The instanceof operator should only be used to compare custom objects from the same JavaScript context. As with the `typeof` operator, any other usage should be avoided.