Introduction
JavaScript Garden - a collection of documentation on the strangest features of the JavaScript language. Here are tips on how to avoid common errors and unobtrusive bugs, as well as problems with speed and wrong programming style.
This is not a language textbook. It is assumed that you already know the language beforehand. For learning the language, I recommend using this great translation of the wonderful book "
Expressive JavaScript ."
Objects
Use and properties of objects
In JS, all entities behave like objects, with two exceptions: null and undefined.
false.toString();
')
It is a common misconception that numbers cannot be used as objects. Due to the imperfection of the parser, it tries to parse the dot after the number as a floating-point notation.
2.toString();
This can be circumvented in different ways:
2..toString();
Objects as a data type
Objects can also be used as hashes (lists of the key-value type). They mainly consist of named properties to which values ​​are attached.
Using {} you can create an empty object. It inherits from Object.prototype and it does not have its own properties.
var foo = {};
Access to properties
Access can be obtained in two ways - through a dot or through square brackets.
var foo = {name: ''} foo.name;
The difference between the two ways is that brackets allow dynamic assignment and use of property names.
Removing properties
You can delete a property only with the delete operator. Assigning it to undefined or null only removes the value of the property, but not the property itself.
var obj = { bar: 1, foo: 2, baz: 3 }; obj.bar = undefined; obj.foo = null; delete obj.baz; for(var i in obj) { if (obj.hasOwnProperty(i)) { console.log(i, '' + obj[i]); } }
The code displays both bar undefined and foo null, but baz does not output - it has been removed.
Reserved words
var test = { 'case': ' , ', delete: ' , !'
Object property names can be written both as simple variables and as strings. Due to another shortcoming of the parser, the code causes a SyntaxError error (for parsers up to ECMAScript 5). Delete is a reserved word, so it must be written as a string.
Prototypes
JS does not have the classical model of inheritance, instead it has prototyping. This is often seen as a language flaw, but in fact this model is more powerful than the classical one. For example, it is enough just to build a classical model based on prototyping, but not vice versa.
JS is the only popular language with prototypical inheritance, so when switching from the classical approach, it may take time to get used to it. The first difference: inheritance in JS uses chains of prototypes.
function Foo() { this.value = 42; } Foo.prototype = { method: function() {} }; function Bar() {}
Note: if you use Bar.prototype = Foo.prototype, then both objects will have a single prototype. Then changes to the prototype of any object will lead to changes in another, and usually this is not what they are trying to achieve.
In the above code, the test object is inherited from both Bar.prototype and Foo.prototype, so it will have access to the method function defined for Foo. As well as access to the value property of an instance of Foo, which is its prototype. It is important to note that the new Bar () does not create a new instance of Foo, but re-uses the one that is assigned to its prototype. Therefore, all Bar instances share the same value property.
Do not use Bar.prototype = Foo, it will not point to the Foo prototype, but to the Foo object. So the prototype chain goes through Function.prototype, not through Foo.prototype. And then the method will not be in the prototype chain.
Property search
When accessing the properties of an object, JS goes up the chain until it finds a property with the specified name.
If he gets to the top, i.e. until Object.prototype, and does not find it, it will return undefined.
Prototype property
Although it is used to build chains, you can still assign any value to it. The assignment of primitives, however, will be ignored.
function Foo() {} Foo.prototype = 1;
But the assignment of objects will work, and lead to the dynamic creation of chains of prototypes.
Speed ​​performance
Finding properties at the top of a chain can be time consuming. In addition, an attempt to reach a non-existent property will always lead to the passage of the entire chain. In addition, when passing a property of an object, each property in the chain will be assigned a number.
Extend built-in prototypes
Sometimes an extension of Object.prototype or some other built-in prototype is improperly used. This is called "monkey patches" and this technique breaks encapsulation. Although it is used by popular frameworks like Prototype, there is no special justification for this clogging of built-in types.
The only reason for the possible extension of embedded prototypes is to embed elements of the new JS engines in the language, for example Array.forEach.
Total
It is important to understand the prototype inheritance model before writing complex code using it. Remember the length of the chains and break them if necessary. No need to extend the built-in prototypes, unless it is done for compatibility with the new properties of JS.
hasOwnProperty
It is used to check the presence of a property directly at the object, and not at one of its prototypes. Inherited by all objects from Object.prototype.
It is not enough to check the property for undefined. The property can exist and still be undefined.
hasOwnProperty is the only thing in the language that works with properties and does not go through a chain of prototypes.
A property called hasOwnProperty is not protected by the language. Because of the possibility that the object has a property with that name, you must use the hasOwnProperty external method to get the correct result.
var foo = { hasOwnProperty: function() { return false; }, bar: ' ' }; foo.hasOwnProperty('bar');
Total
Using hasOwnProperty is the only reliable method for determining whether an object has a property. Its use is recommended in many cases when traversing the property of objects.
For in loop
Like the in operator, the for in loop runs through a chain of prototypes when traversing the properties of an object. The loop will not go through properties whose enumerable attribute is false — for example, like the length property of arrays.
Since it is impossible to change the work for in, it is necessary to filter unnecessary properties in the body of the loop. With ECMAScript 3, the hasOwnProperty method from Object.prototype is used for this.
With ECMAScript 5, Object.defineProperty can be used if enumerable is set to false to add properties to objects without being numbered. In this case, it is reasonable to assume in the code that any numbered properties are added for a reason, and omit hasOwnProperty, since it reduces the readability of the code. In the library code hasOwnProperty it is necessary to use, because there it is impossible to make assumptions about what numbered properties can be in the chain of prototypes.
Since for in passes through the entire chain, it will work more slowly with each inheritance layer.
Use hasOwnProperty to filter
This code can only be used with older versions. Due to the use of hasOwnProperty, only moo will be displayed. If you remove hasOwnProperty, then errors may appear in the code due to a possible extension of the prototypes.
In new versions of ECMAScript, non-numbered properties can be set via Object.defineProperty, and the risk of passing through properties can be reduced without using hasOwnProperty. And you still have to be careful, using old libraries like Prototype, which do not use the capabilities of the new ECMAScript.
Total
It is recommended to always use hasOwnProperty in ECMAScript 3 and below, as well as in library code. Starting with ECMAScript 5, Object.defineProperty makes it possible to define non-indexable properties, so that hasOwnProperty can be omitted.