Великолепный JavaScript
Здесь постараюсь описать странности JavaScript, со многими из которых я уже успел смирится, а некоторые понять и простить. Например самое любимое это:
a = null;
typeof a; // "object"
это довольно популярная “шутка” является банальным багом языка, который закопан в нем видимо навсегда для сохранения обратной совместимости.
Приведение массива к строке
Здесь будет просто христоматийный пример из книги “You don’t know JavaScript”:
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false
При сравнении массива и строки в динамически типизированном JavaScript массив будет преобразован в строку путем конкатенации всех значений через запятую, т.о. массивы a и b после приведения к строке будут иметь значение эквивалентное переменной c. Проверяем:
> [1,2,3].toString()
'1,2,3'
Массивы в JavaScript это объекты, помните?
> typeof [1,2,3]
'object'
Объекты хранятся и сравниваются по ссылке, т.е. переменные a и b это два разных объекта.
Смотрим 11.9.3 The Abstract Equality Comparison Algorithm спеки EcmaScript5:
Return true if x and y refer to the same object. Otherwise, return false.
Function declaration vs Function expression
Есть два способа обьявить ф-цию в JavaScript
- Function expression
let foo = function() {}
- Function declaration
function foo() {}
Что важно помнить так то что Function declaration hoisted - что значит что функция будет поднята вверх своей области видимости в отличии от function expression.
Пример:
foo();
function foo() {
console.log('Func "foo" called');
} // no need for semicolon
try {
bar(); // TypeError: bar is not a function
} catch (e) {
console.log(e.name+': '+e.message);
}
var bar = function() {
console.log('Func "bar" called');
}; // semicolon is needed here
bar();
На выходе получим следующее:
Func "foo" called
TypeError: bar is not a function
Func "bar" called
Scoping
В JavaScript можно выделить такие виды области видимости:
- Global Scope
- Block Scope
- Function Scope
- Lexical Scope
С первым надеюсь все понятно - использовать его нужнотолько в крайнем случае (читай НЕ нужно) четко понимая зачем и почему, к-во переменных и функций здесь напрямую влияет на производительность, ну и name collision тут чаще всего может произойти. К тому же полагатся на Global Scope во времена dependency injection считается bad practice.
Block Scope появился сравнительно недавно, с вводом в ES-2015 блочных директив let
и const
.
Function Scope можно обьяснить довольно просто - все что пришло в ф-ю или создано внутри нее в ней останется навсегда и снаружи не видно.
Lexical Scope более сложный для понимания и тесно связан со всеми остальными видами области видимости. Фактически это о вложенности областей видимости. Например переменные из parent function будут доступны во вложенной функции. Для того чтобы совсем всех запутать, пример ниже:
let adder = function parent(total) {
return function child(number) {
total += number;
console.log(total); // debug
return total;
};
}(0); // set initial total value
adder(2); // 2
adder(3); // 5
adder(5); // 10