Serhii Polishchuk profile picture Serhii Polishchuk

Великолепный JavaScript

Jan 27, 2018

Здесь постараюсь описать странности 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