Skip to content

Latest commit

 

History

History
114 lines (81 loc) · 7.34 KB

File metadata and controls

114 lines (81 loc) · 7.34 KB

Вы не знаете JS: Область видимости и замыкания

Приложение C: Лексический this

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

ES6 добавляет особую синтаксическую форму объявления функции, названную "стрелочная функция". Она выглядит примерно так:

var foo = a => {
	console.log( a );
};

foo( 2 ); // 2

Так называемая "жирная стрелка" часто упоминается как сокращение для утомительно длинного (сарказм) ключевого слова function.

Но есть кое-что более важное в стрелочных функциях, что не имеет ничего общего с экономией символов в вашем коде.

В двух словах, этот код страдает от одной проблемы:

var obj = {
	id: "awesome",
	cool: function coolFn() {
		console.log( this.id );
	}
};

var id = "not awesome";

obj.cool(); // awesome

setTimeout( obj.cool, 100 ); // not awesome

Проблема заключается в потере привязки this в функции cool(). Есть разные пути решения этой проблемы, но наиболее частое решение — var self = this;.

Выглядит это примерно так:

var obj = {
	count: 0,
	cool: function coolFn() {
		var self = this;

		if (self.count < 1) {
			setTimeout( function timer(){
				self.count++;
				console.log( "красиво?" );
			}, 100 );
		}
	}
};

obj.cool(); // красиво?

Чтобы не слишком углубляться в подробности, "решение "var self = this всего лишь избавляется целиком от всей проблемы понимания и правильного использования привязки this, а взамен возвращается к чему-то более удобному для нас: лексической области видимости. self становится всего лишь идентификатором, который может быть определен с помощью лексической области видимости и замыкания, и не заботится о том, что случится с привязкой this по пути.

Люди не любят писать подробно особенно то, что делают снова и снова. Таким образом, мотивацией ES6 является помощь в облегчении таких сценариев и разумеется в устранении общих проблем идиом, таких как эта.

Решение в ES6, стрелочная функция, вводит поведение, называемое "лексический this".

var obj = {
	count: 0,
	cool: function coolFn() {
		if (this.count < 1) {
			setTimeout( () => { // стрелочная функция, выигрышный вариант?
				this.count++;
				console.log( "красиво?" );
			}, 100 );
		}
	}
};

obj.cool(); // красиво?

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

Так что в этом примере кода стрелочная функция не получает свой this непривязанным каким-то непредсказуемым путем, она всего лишь "наследует" привязку this функции cool() (что правильно, если мы вызываем ее так, как показано выше!).

Несмотря на то, что это делает код короче, моя точка зрения в том, что стрелочные функции — всего лишь на самом деле закодированная в синтаксис языка распространенная ошибка разработчиков, которая приводит к тому, чтобы запутать и соединить правила "привязки this" с правилами "лексической области видимости".

Другими словами: зачем искать неприятности и ударяться в словоблудие используя парадигму кодирования стиля this, всего лишь чтобы подрезать ему крылья смешивая его с лексическими ссылками. Кажется естественным принять тот или иной подход для любой конкретной части кода, а не смешивать их в одном и том же месте.

Примечание: еще один недостаток стрелочных функций в том, что они анонимны, не именованы. Загляните в главу 3, чтобы ознакомиться с причинами почему анонимные функции менее предпочтительны, чем именованные.

Более подходящий подход, с моей точки зрения, к этой "проблеме", использовать и рассматривать механизм this правильно.

var obj = {
	count: 0,
	cool: function coolFn() {
		if (this.count < 1) {
			setTimeout( function timer(){
				this.count++; // `this` безопасен из-за `bind(..)`
				console.log( "еще красивее" );
			}.bind( this ), 100 ); // смотри, `bind()`!
		}
	}
};

obj.cool(); // еще красивее

Что бы вы ни предпочли: новое поведение лексического this стрелочных функций или испытанный и верный bind(), важно отметить, что стрелочные функции — не только сокращение написания "function".

У них есть намеренная разница в поведении, которую необходимо изучить и понимать, и если мы их выбираем, то использовать по максимуму их возможности.

Теперь, когда мы полностью понимаем образование лексической области видимости (и замыкания!), понять лексический this будет проще простого!