I really like JavaScript and I find it powerful and convenient. But for most novice JS programmers, many problems create a misunderstanding of aspects of the language. Often, language constructs behave "illogically." In this article I want to give examples of "rakes" that I stepped on; explain the behavior of the language and give a couple of tips.
Types
As written in
the ECMAScript specification , there are a total of 6 types:
- Undefined
- Null
- Boolean
- String
- Number
- Object
All values ​​must belong to them. In JS there is a typeof operator, which, as it would seem, should return the type of an object. It would seem one of the listed. What actually happens:
')
typeof 5;
Problem: although the type is null - Null, the operator returns 'object'; and the type of the function is Object, the operator returns 'function', but there is no such type.
Explanation: typeof returns not a type, but a string that depends on the argument and is not a type name.
Tip: forget the types. Seriously, I think that knowing the 6 types of JS will not give you any benefit, and the typeof operator is used quite often, so it’s better to remember the results of its work:
Argument type | Result |
Undefined | undefined |
Null | object |
Boolean | boolean |
Number | number |
String | string |
Object (results of the operator new, inline-objects ({key: value})) | object |
Object (functions) | function |
Magic Values: undefined, null, NaN
The
specification is described as follows:
- undefined value - the value of the primitive is used
- Undefined type - type
- null value - the value of the object value
- Null type is the null value
- NaN - number value that is a IEEE 754 “Not-a-Number” value
In my head, I keep the following:
- undefined - the value of a variable that has not been initialized. Single value of type Undefined.
- null - an intentionally created “empty” object. The only value of type null.
- NaN is a special value of the Number type, for the expression “not numbers”, “uncertainty”. It can be obtained, for example, as a result of dividing 0 by 0 (from the course of mathematical analysis, remember that this is uncertainty, and the division of other numbers by 0 is infinity, for which in JS there are Infinity values).
With these meanings, I discovered a lot of "magic." For starters, boolean operations with them:
!!undefined;
Problem: whatever we compare NaN, the result of the comparison will always be false.
Explanation: NaN can occur as a result of multiple operations: 0/0, parseInt ('irreducible string'), Math.sqrt (-1) and it would be strange if the root of -1 was 0/0. That is why NaN! == NaN.
Tip: do not use boolean operators with NaN. For verification, you must use the isNaN function.
typeof a;
Problem: the typeof operator tells us that the type of an undeclared variable is undefined, but an error occurs when accessing it.
Explanation: in fact, there are 2 concepts - Undefined and Undeclared. So, an undeclared variable is an Undeclared variable and accessing it causes an error. The declared but not initialized variable takes on the value undefined and no errors occur when accessing it.
Tip: Before accessing a variable, you must be sure that it is declared. If you refer to the Undeclared variable, the code following the call will not be executed.
var a;
Problem: at any moment we can read and write the value undefined, therefore, someone can overwrite it for us and the comparison with undefined will be incorrect.
Explanation: undefined is not only the undefined value of the Undefined type, but also a global variable, which means that anyone can override it.
Tip: just comparing variables with undefined is a bad tone. There are 3 options for solving this problem, to create "bulletproof" code.
- You can compare not the value of a variable, but its type: “typeof a === 'undefined'”.
- Use the pattern immediately-invoked function:
(function(window, undefined){
- To get a real “undefined” value, you can use the void operator (by the way, I don’t know of another use for this operator):
typeof void(0) === 'undefined'
Now let's try to perform similar actions with null:
console.log(null);
Problem: despite some similarities between null and undefined, we cannot overwrite null. In fact, this is not the problem, but the fact that the language behaves illogically: allows you to overwrite undefined, but does not allow overwriting null.
Explanation: null is not a global variable and you cannot create it, because null is a reserved word.
Tip: there are not so many reserved words in JavaScript, it's easier to
remember them and not to use them as variable names, than to delve into what the problem is when it comes up.
And now let's do the same with NaN:
console.log(NaN);
Problem: when redefining undefined everything went well, when redefining null an error occurred, and when redefining NaN, the operation did not cause an error, but the property was not redefined.
Explanation: you need to understand that NaN is a global context variable (the window object). In addition, you can “reach out” to NaN via Number.NaN. But it does not matter, none of these properties you can not override, because NaN is not a writable property:
Object.getOwnPropertyDescriptor(window, NaN).writable;
Tip: as a JS programmer, you need to know about
property attributes :
Attribute | Type of | Meaning |
enumerable | Boolean | If true, this property will participate in for-in loops. |
writable | Boolean | If false, the value of this property cannot be changed. |
configurable | Boolean | If false, then the value of this property cannot be changed, the property attributes can not be deleted or changed. |
value | Any | Property value when reading |
get | Object (or Undefined) | getter function |
set | Object (or Undefined) | setter function |
You can declare properties that are not deleted or read-only for objects created by you using the Object.defineProperty method:
var obj = {}; Object.defineProperty(obj, 'a', {writable: true, configurable: true, value: 'a'}); Object.defineProperty(obj, 'b', {writable: false, configurable: true, value: 'b'}); Object.defineProperty(obj, 'c', {writable: false, configurable: false, value: 'c'}); console.log(obj.a);
Work with fractional numbers
Let's remember the 3rd grade and add a few decimal fractions. The results of the addition in mind check in the JS console:
0.5 + 0.5;
Problem: when adding some fractional numbers, an arithmetically incorrect result is given.
Explanation: such results are obtained due to the peculiarities of working with floating point numbers. This is not a JavaScript feature, other languages ​​work as well (I checked in PHP, Python and Ruby).
Tip: First, you, as a programmer,
must know about the features of a floating-point computer. Secondly, in most cases it is enough to simply round the results. But if you suddenly need to give the user an exact result, for example, when working with money data, you can simply multiply all the arguments by 10 and divide the result back by 10, like this:
function sum() { var result = 0; for (var i = 0, max = arguments.length; i< max; i++ ) { result += arguments[i]*10; } return result / 10; } sum(0.5, 0.5);
Conclusion
These are just a few unusual examples with unpredictable results. If you remember them, it will turn out not to step on the same rake and quickly understand what the problem is. If you find a new “illogical” piece of code, then try to understand what is happening from the point of view of the language by reading the
specification or
MDN .