This in JavaScript. Часть 1

Konstantin
Konstantin Ostrovsky
2018-09-29 14:58:01
3

Контекст в JavaScript представляет собой переменные области видимости. Сегодня наша цель - выяснить используете ли Вы все возможности такого мощного инструмента как контексты.

Под катом: ООП в js, функции call и apply, this, let, стрелочные функции, блочные области видимости.

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

Самым плохим примером игнорирования рекомендации в ограничении области видимости является использование глобального контекста window. Начинающие программисты могут использовать глобальную область видимости, тем самым повышая шанс потери данных, а так же неразумного потребления ресурсов браузера. Браузер очищает место в оперативной памяти, занимаемое объектом, после его диструктуризации. Контекст window будет "жив" до того момента пока вы не обновите окон браузера, следовательно все переменные его окружения будут излишне долго храниться в оперативной памяти. Window - это самый верхний уровень контекст браузера. Все переменные созданные без ограничения области видимости попадают в объект window. Пример:

var verysecret = "im very hight varible!";
console.log(window.verysecret)
// "im very hight varible!"

Таким образом мы видим что переменная "verysecret" попала в глобальный контекст. Этого можно избежать если "обернуть" создание переменной в лямбду(анонимную функцию - это функция которая вызывается сразу после инициализации).

(function(){
   var verysecret = "im very hight varible!";
})();

console.log(verysecret);
// undefined

console.log(window.verysecret);
// undefined

Переменная не только не попала в глобальную область видимости, но и ограничила свой контекст лямбдой. Переменная доступна только внутри "{...}" блока в котором она инициализирована.

Отлично! Теперь мы можем не переживать что другой разработчик или плагин использует такое же название переменной и может "затереть" значение нашей переменной.

Давайте попробуем инициализировать эту переменную в объект this контекста функции.

(function(){
   this.verysecret = "im very hight varible!";
   console.log(this.verysecret);
   // "im very hight varible!"
})();

ООП в JavaScript

ООП в JavaScript - есть и я попробую Вам это доказать!

Объявим объект Cat, который будет иметь в своем контексте свойства {name, color} и метод "meow". Код функции:

function Cat(options = {}) {
  this.name = (options.name) ? options.name : "";
  this.color = (options.color) ? options.color : "";

  this.meow = () => {
    console.log(`${this.name} say meow...`);
  }
}

var catOptions = { name: "Barsik", color: "white" },
Barsik = new Cat(catOptions);

Барсик - экземпляр(условная единица) объекта Cat. И при создании объекта Cat мы передали параметрами его имя и цвет. Далее эти значения запишутся в переменные контекста и будут доступны из в переменной "Barsik".

console.log(Barsik);
// Cat {name: "Barsik", color: "white", meow: ƒ}

console.log(Barsik.color);
// white

Такая реализация функций JavaScript очень напоминает классы и работает по схожим сценариям, только имеет более примитивные возможности по сравнению с полноценным ООП: наследованием, полиморфизмом и прочими терминами, которые так любят HR. В ES6 есть хорошая поддержка классов, но поддержка браузерами оставляет желать лучшего, поэтому я бы не рекомендовал их использовать без Babel. Более подробно о классах в JavaScript вы можете прочитать здесь.

()={} Стрелочные функции

ES6 принес нам не только классы, но и стрелочные функции. Стрелочные функции представляю собой функции, не имеющие собственного контекста. Стрелочные функцию являются мощным инструментом для передачи контекста из родительской функции. Давайте рассмотрим стрелочные функции на примере setTimeout.

function consoleAfterTimeout(text) {
  this.text = `Hello! i like talk you ${text}`; // this - контекст consoleAfterTimeout

  setTimeout(function() {
    console.log(this.text); // this - контекст setTimeout
  }, 500);
}

var say =  new consoleAfterTimeout("you are smart!");
// undefined

Таким образом можно заметить что функция consoleAfterTimeout и setTimeout имеют разные контексты и функция setTimeout не имеет доступ к контексту функции consoleAfterTimeout. Во избежание этого можно использовать стрелочную функцию как callback для функции setTimeout. Пример:

function consoleAfterTimeout(text) {
  this.text = `Hello! i like talk you ${text}`; // this - контекст consoleAfterTimeout

  setTimeout(() => {
    console.log(this.text); // this - контекст setTimeout
  }, 500);
}

var say =  new consoleAfterTimeout("you are smart!");
// Hello! i like talk you you are smart!

Стрелочные функции - очень мощный инструмент в арсенале FrontEnd-разработчика. Как Вы видите, стрелочные функции делают JavaScript еще более гибким и удобным.

Блочные области видимости и let вместо var

Зачастую в разработке нам приходится использовать большое количество "одноразовых" переменных для хранения временных данных. Самый распространенный пример - это цикл for. Рассмотрим на примере:

for (var i = 1; i <= 5; i++) {
	console.log(i); // 1 2 3 4 5
}
console.log(i); // 6

После выполнения цикла в области видимости остается переменная "i", которая нам больше не пригодится, но все равно занимает ресурсы браузера и хранится в оперативной памяти. И тут на помощь нам приходит объявление переменной при помощи "let". Переменная объявленная при помощи let ограничивает свою область видимости фигурными скобками, в которых она находится. Выглядеть это решение будет следующим образом:

for (let i = 1; i <= 5; i++) {
	console.log(i); // 1 2 3 4 5
}
console.log(i); // undefined

Еще один необычный пример использования let:

var count = 0;
if(true) {
	let count = 50;
	console.log(count); // 50
    // sendDataToServer(count); // Какая-то функция которая отправила что-то куда-то
}

console.log(count); // 0

Используя let можно не переживать о пересечении имен переменных в контекстах.

Условие выглядит странно, не так ли?

Условие которое всегда выполняется и служит лишь для создания блочной области видимости. В ES6 предусмотрели подобный исход и добавили блочные области видимости. Это просто фигурные скобки, которые служат для создания отдельной области видимости. Легче это будет понять на примере:

var count = 0;
{
	let count = 50;
	console.log(count); // 50
    // sendDataToServer(count); // Какая-то функция которая отправила что-то куда-то
}

console.log(count); // 0

Вы всегда можете избежать использования нововведений и новых возможностей языка, добавляя в свой код превосходные костыли, но каждый разработчик должен знать о новых возможностях языка хотя бы для того что бы уметь понимать чужой код и поддерживать проекты, написанные с использованием всех возможностей ES6.