Pull to refresh

JavaScript в диаграммах (Часть 1)

JavaScript
Sandbox
Одним из секретов эффективного JavaScript-разработчика является глубокое понимание семантики языка. В этой статье я объясню основные элементарные части языка, используя максимально простые и понятные диаграммы.

Повсюду ссылки


Перменная в JavaScript — просто имя, указывающее на значение, хранящееся где-то в памяти. Эти значения могут быть как примитивами (строками, целыми числами, булевыми), так и объектами или функциями.

Локальные переменные

В следующем примере мы создадим четыре локальных переменных в области видимости (scope) высшего уровня и укажем их на некоторые примитивные значения:

// создадим несколько локальных переменных в области видимости верхнего уровня
var name = "Tim Caswell";
var age = 28;
var isProgrammer = true;
var likesJavaScript = true;
// Проверим, указывают ли две последние переменные на одно и то же примитивное значение
isProgrammer === likesJavaScript;

Output
=> true



Обратите внимание, что две переменные указывают на одно и то же значение в памяти. Это происходит потому что примитивы неизменяемы и виртуальная машина может исользовать один экземпляр объекта для всех ссылок-переменных, которые указывают на это значение.

В примере выше мы проверили, указывают ли две ссылки на одно и то же значение, используя оператор === и получили подтверждение true.

Прямоугольник слева в диаграмме — внешняя замкнутая область видимости (closure scope) высшего уровня. Переменные в ней — локальные переменные высшего уровня, важно не путать их со свойствами (properties) объекта global/window.

Объекты и цепочки прототипов

Объекты — просто наборы ссылок на другие объекты и прототипы. Единственное отличие заключается в добавлении цепочки прототипов (prototype chain), чтобы получить доступ к свойствам, находящимся не в локальном объекте, а в родительском.

// Создадим родительский объект
var tim = {
    name: "Tim Caswell", 
    age: 28,
    isProgrammer: true,
    likesJavaScript: true
}
// Создадим дочерний объект
var jack = Object.create(tim);
// Локально переназначим некоторые свойства
jack.name = "Jack Caswell";
jack.age = 4;
// Теперь поищем какое-нибудь свойство в цепочке прототипов
jack.likesJavaScript;

Output
=> true



Здесь у нас есть один объект с четырьмя свойствами, на который ссылается переменная tim. Также мы создали новый объект, наследующий первый и ссылаемся на него переменной jack. После этого переписываем два свойства в локальном объекте.

Теперь, если мы начинаем искать свойство jack.likesJavaScript, сначала находим объект, на который указывает jack. Далее ищем свойство likesJavaScript. Так как его там нет, смотрим в родительский объект и находим его там и получаем значение true, на которое это свойство ссылается.

Глобальный объект

Если вы когда-нибудь задумывались, почему инструменты типа jslint всегда советуют не забывать ставить выражение var перед объявлением переменной, то вот что случается в противном случае:


var name = "Tim Caswell";
var age = 28;
var isProgrammer = true;
// Забываем поставить var 
likesJavaScript = true;




Обратите внимание, что likesJavaScript теперь свойство глобального объекта, вместо того, чтобы быть свободной переменной во внешней замкнутой области видимости. Это имеет значение только тогда, когда вы миксуете несколько скриптов. Но в реальных программах это именно то, что вы собираетесь делать, не так ли?

Запомните: всегда ставьте выражения var, чтобы удержать переменную в текущем и дочерних замкнутых областях видимости.

Если вам нужно положить что-то в глобальный объект, сделайте это специально, используя window.woo в браузере или global.woo в node.js

Функции и области видимости

JavaScript — это не просто набор связанных структур данных. Он содержит исполняемый, вызываемый код, известный как функции. Функции создают связанные области видимости и замыкания.

Визуализация замыканий

Функцию можно изобразить специальным объектом, содержащим не только свойства, но и исполняемый код. Каждая функция имеет специальное свойство [scope] (область видимости), которое представляет среду, в которой находилась функция в момент объявления. Если функция возвращена из другой функции, то эта самая ссылка на среду, откуда ее вернули «закрыта» новой функцией в «замыкание».

В этом примере мы создадим простой factory-метод, генерирующий замыкание и возвращающий функцию.


function makeClosure(name) {
    return function () {
      return name;
    };
}
var description1 = makeClosure("Cloe the Closure");
var description2 = makeClosure("Albert the Awesome");
console.log(description1());
console.log(description2());

Output
Cloe the Closure Albert the Awesome



Когда мы вызываем description1() виртуальная машина ищет функцию по этой ссылке и исполняет ее. Эта функция ищет локальную переменную, названную name, и находит ее в замкнутой области видимости. Этот factory-метод хорош тем, что каждая сгенерированная функция имеет свою область видимости для локальных переменных.

Если хотите узнать больше о замыканиях, смотрите в моей статье why use closure (прим.пер. если будет интересно, переведу и эту статью).

Общие функции и this

Иногда по причинам произдводительности или предпочтений в стиле программирования используются общие (shared) функции, которые позволяют использовать одну и ту же функцию в разных областях видимости, при помощи ключевого слова this.

Создадим пару объектов, которые делят общую функцию. В этой функции будут использоваться указатели на this, чтобы показать разницу.


var Lane = {
    name: "Lane the Lambda",
    description: function () {
      return this.name;
    }
};
var description = Lane.description;
var Fred = {
    description: Lane.description,
    name: "Fred the Functor"
};
// Вызваем функцию из разных областей видимости
console.log(Lane.description());
console.log(Fred.description());
console.log(description());
console.log(description.call({
    name: "Zed the Zetabyte"
}));

Output
Lane the Lambda Fred the Functor undefined Zed the Zetabyte



В этой диаграмме мы видим, что хотя Fred.description было присвоено Lane.description, это всего лишь ссылка на функцию. Таким образом, все три ссылки указывают на одну общую анонимную фунцию. Вот почему я стараюсь не назвать функции в конструкторах прототипов «методами», это может ввести в заблуждение о некоторой привязке фунцкиии к конструктору и его «классу».

Если хотите узнать больше о this, смотрите в моей статье what is this (прим.пер. если будет интересно, переведу и эту статью).

Заключение

Надеюсь, это поможет изучающим JavaScript глубже понять семантику языка. В прошлом я был и разрабочтиком/дизайнером клиентской части, и архитектором серверной, поэтому я выбрал особый визуальный путь объяснения.

Оригинал статьи от Tim Caswell: howtonode.org/object-graphs

От переводчика: это моя первая статья и перевод на Хабре, самому весь цикл статей показался очень полезным. У автора есть еще две части и много других материалов на сайте howtonode.org. Если есть неточности или поправки, исправлю. Не совсем уверен в правильности перевода closure (замыкание) и scope (пространство), может быть у кого-то есть лучшие версии? Если будет интересно, переведу остальные две части.

UPD: Исправил опечатки, привел в порядок примеры кода и по совету ilya42 заменил «пространства» на более правильные «области видимости».


Tags:javascriptnode.js
Hubs: JavaScript
Total votes 55: ↑48 and ↓7 +41
Views1.7K

Popular right now

Top of the last 24 hours