Область видимости переменных javascript this

this в JavaScript — что такое контекст

Одна из причин, по которой ключевое слово this такое не простое для многих программистов JavaScript — даже опытных — заключается в том, что к чему относится this , полностью зависит от контекста, иногда довольно сложным образом. Так, чтобы разобраться с this , важно не только понять, как «this» ведет себя в различных контекстах, но и понять сами контексты — и как их определить в живом коде.

Теоретические основы

this — это ключевое слово в JavaScript которое содержит в себе объект (контекст) выполняемого кода.

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

this имеет различные значения в зависимости от того, где используется:

  • Сама по себе — this относится к глобальному объекту (window).
  • В методе — this относится к родительскому объекту.
  • В функции — this относится к глобальному объекту.
  • В функции в ‘strict mode’ — this = undefined.
  • В стрелочной функции — this относится к контексту где функция была создана.
  • В событии — this ссылается на элемент запустивший событие.

Что такое контекст?

Любое определение this, так или иначе крутится вокруг слова контекст. Давайте разберемся что же это такое.

JavaScript является одно-поточным языком, то есть одновременно может выполняться только одна задача. Интерпретатор JavaScript всегда начинает выполнять код с глобального контекста (в браузере это объект window). С этого момента у самой первой вызванной функции контекстом будет глобальный объект (window). Наша функция может создать новый объект, создать в нем методы и запустить один из этих методов, теперь контекст вызова изменился и стал указывать на новый объект, который был создан из глобального контекста.

Таким образом, контекстом всегда является какой-то объект из под которого был вызван метод (функция).

По ходу обработки кода внутри глобального контекста window создаются другие объекты, они представляют собой новые контексты в которых выполняется код. Таким образом, в одном коде может быть замешено много контекстов. Одна функция может вызывать методы разных объектов и у каждого из них будет свой контекст.

Контекст — это всегда значение ключевого слова this, которое является ссылкой на объект, который запустил метод (функцию). Контекст — это объект «владеющий» исполняемым кодом. А this всегда ссылаться на объект (контекст) запустивший функцию.

Методы и функции

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

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

  • Неудобно каждый раз писать в коде название родительского объекта, чтобы вызвать его метод (функцию), а можно написать this.my_function() вместо window.my_object.my_function() .
  • Иногда мы вообще можем заранее не знать объект, который вызвал функцию. Потому что программист написал код так, что объект (контекст) нужно указать при вызове функции (этот подход позволяет использовать одну и ту же функцию/метод, для разных объектов).
Читайте также:  Html div align valign

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

Важно понять, что this не зависит от того, где была объявлена функция, а зависит от того, как (кем) функция была вызвана. То где функция была объявлена имеет отношение к области её видимости (scope), а не к контексту.

Область видимости (scope) и Контекст

Кроме контекста нужно еще понимать что такое область видимости. Эти понятия часто путают. Называя одно другим. Однако контекст и область видимости это разные вещи.

Каждый вызов функции имеет как область видимости, так и контекст, связанный с ней. Область видимости основана на том, где функция вызывается (какие переменные ей доступны), а контекст основан на том, кем функция вызывается (каким объектом). Другими словами, область видимости относится к доступу функции к переменным при ее вызове и является уникальной для каждого вызова. Контекст — это всегда значение ключевого слова this, которое является ссылкой на объект, «владеющий» текущим исполняемым кодом.

this по умолчанию

Чаще всего this используется внутри функции/метода. Однако this также работает в глобальной области.

Когда мы пишем простую функцию и затем её используем, она все равно вызывается каким-то объектом и this ссылается на этот вызывающий объект. Для браузера — это объект window.

Запустим такой код в консоли браузера:

function get_this() < return this >get_this() // ▸ Window this // ▸ Window

Как мы видим в обоих случаях this будет объектом Window. Это равносильно такому коду:

window.get_this = function() < return this >window.get_this() // ▸ Window window.this // ▸ Window

Однако если функция выполняется в строгом режиме, то в this будет записано undefined , так как в этом режиме запрещены привязки по умолчанию:

function get_this() < 'use strict' return this >get_this() // ▸ undefined window.get_this() // ▸ Window

Т.е. в строгом режиме нужно прямо указывать контекст из которого вызывается метод:

this в методах

Метод вызывается наглядно: сначала идет название объекта ourObject , затем точка . , затем название метода ourMethod .

this очень легко отследить, когда метод вызывается в сочетании с объектом object.method() — что находится с левой стороны от точки, то и будет в this вызываемого метода (в данном случае this = object):

let object = < method: function()< console.log( this ) >> object.method() // ▸

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

this в функции-конструктора

Когда для создания новых объектов используется функция, её нужно вызывать с помощью ключевого слова new. При таком вызове функция будет вызвана как конструктор нового объекта и новый объект будет возвращен.

Читайте также:  Java class forname jdbc mysql

Название функции в этом случае принято писать с заглавной буквы. Это название станет названием экземпляра объекта.

Когда вызывается конструктор, this указывает на вновь созданный объект, а не на объект который запустил построение.

function Doge( param ) < // this = <>— новый пустой объект, который вызвал эту функцию благодаря new this.saying = param // return this // это конструктор, он не может ничего возвращать > new Doge( ‘Привет’ ) // ▸ Doge new Doge( ‘Браузер’ ) // ▸ Doge

Заглянем под капот, чтобы лучше понять почему this в функции-конструктора работает именно так. Когда функция вызывается через new вызов этой функции происходит иначе:

  1. Создается пустой объект, название которого берется из названия функции.
  2. Созданный объект вызывает функцию как конструктор. Так как функцию вызывает новый объект, то this внутри этой функции ссылается на этот новый объект.
  3. Функция-конструктор отрабатывает и new возвращает вновь созданный объект.

В такой функции не нужно использовать return — в этом нет смысла, потому что она является конструктором объекта. В задачи функции-конструктора входит установка свойств и методов нового объекта и она не может ничего возвращать.

Усложним: вызов функции внутри конструктора

Что будет если мы поместим обычную функцию внутрь функции-конструктора и вызовем её там?

У нас есть глобальная функция getThis() , функция-конструктора и новый объект, созданный функцией-конструктора. В этом примере глобальная getthis() вызываются изнутри функции-конструктора:

function get_this() < console.log( this ) >function Doge( saying ) < this.saying = saying get_this() >new Doge( ‘Не Window!’ ) // увидим в консоли: // ▸ Window // ▸ Doge

get_this() все еще указывает на Window, потому что, несмотря на то что get_this() вызывается внутри функции конструктора Doge() , она фактически вызывается из глобальной области — window.get_this() .

А что, если мы создадим метод внутри конструктора и вызовем его в конструкторе при создании нового объекта?

function Doge( saying ) < this.saying = saying this.get_this = function()< console.log( this ) >this.get_this() > new Doge( ‘не Window’ ) // создадим экземпляр Doge // ▸ Doge // ▸ Doge

get_this() по-прежнему указывает на вновь созданный объект. Потому что именно он его вызвал.

А что если, мы вызовем Doge без ключевого слова new?

function Doge( saying ) < this.saying = saying this.get_this = function()< console.log( this ) >this.get_this() console.log( this ) > Doge( ‘Кто здесь?’ ) // запускаем Doge как простую функцию // Получим в консоли: // ▸ Window // ▸ Window

Как мы видим функция была вызвана объектом window и она добавила в вызываемый объект window новое свойство saying и метод get_this() . Убедимся в этом, посмотрим значение свойства saying и вызовем метод get_this().

window.saying // «Кто здесь?» window.get_this() // Window

Итого про this

Значение this устанавливается в зависимости от того, как вызвана функция:

При вызове функции в качестве метода, контекстом будет вызываемый объект:

obj.func(. ) // this = obj obj["func"](. ) // this = obj

При обычном вызове, контекстом будет глобальный объект она будет вызвана без контекста:

func(. ) // this = window (ES3) / undefined (ES5 - 'use strict')

При использовании ключевого слова new, создается новый чистый контекст.

new func() // this = <> (новый объект)

Источник

Читайте также:  Javascript form checked value

Область видимости переменной в Javascript (ликбез)

Переменные в Javascript бывают глобальными и локальными. Глобальная переменная доступна везде, локальная — только в текущей области видимости.

Технически, глобальные переменные — всего лишь свойства объекта window , поскольку весь код выполняется в его контексте.

  

Из этого следует, что глобальные переменные могут затирать свойства window (я уже молчу о том, что они зло, нарушают инкапсуляцию и все такое).

Объявление переменных

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

function foo() < a = 2; b = 3; return a+b; >alert(a); // undefined a = 'очень важное значение'; alert(a); // очень важное значение foo(); alert(a); // 2

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

Явно объявлять переменные можно и нужно ключевым словом var .

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

function foo() < var a = 2; var b = 3; return a+b; >alert(a); // undefined var a = 'очень важное значение'; alert(a); // очень важное значение foo(); alert(a); // очень важное значение

Как объявить глобальную переменную из функции? Как обратиться к глобальной переменной, если есть локальная с таким же именем? Очень просто — нужно обратиться к ней как к свойству window :

function foo() < var location = 'location'; alert(location); // вернет 'location' alert(window.location); // вернет window.location window.a = 'переменная из функции'; >alert(a); // undefined foo(); alert(a); // переменная из функции

Наследование области видимости

Меня всегда смущало то, что в Javascript можно определять функции внутри функций, а использовать их потом где угодно. Ну да, если посмотреть, точно то же самое можно делать в Ruby, и, наверное, во многих других языках тоже.

Переменные при этом передаются очень просто: если на момент определения функции переменная существовала, то она будет существовать и внутри функции. Откуда бы ее не вызывали.

function alertOnTimeout(message, timeout) < return setTimeout(function() < // message будет доступен в безымянной функции, переданной таймауту alert(message); >, timeout); >

Передача кода по старинке — строкой, которая прогоняется через eval() — не попадает под это правило, код исполняется в той области видимости, где и определен.

Поскольку объекты в Javascript — это тоже типа функции, то свойство объекта определяется точно так же, как и переменная.

А еще в Javascript область видимости переменной ограничивается только функциями, а не блоками типа if (привет, Паскаль). Потому удобнее всего объявлять переменные в начале функции.

this

А что this ? А то, что эта переменная автоматически появляется в методах объектов и затирает значение this из предыдущей области видимости. Решение простое — переприсваивать ее значение другой переменной.

$('div.with-links').click(function() < var theDiv = this; //сохраняем значение this $(this).find('a').click(function() < alert($(this).attr('href')); // this - это ссылка theDiv.remove(); // а theDiv - это все еще дивак >); >);

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

Источник

Оцените статью