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

в 8:38, , рубрики: javascript, node.js, Песочница, метки: ,

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

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

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

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

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

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

Output
=> true

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

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

В примере выше мы проверили, указывают ли две ссылки на одно и то же значение, используя оператор === и получили подтверждение 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

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

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

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

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

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

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

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

Обратите внимание, что 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

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

Когда мы вызываем 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

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

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

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

Заключение

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

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

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

Автор: verbovkov

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js