Что такое this
Перейти к содержимому

Что такое this

  • автор:

Ключевое слово this в JavaScript. Полное* руководство

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

image

  1. Введение
  2. Заблуждения о this
  3. Как определить значение this

Введение

Ключевое слово this — одна из наиболее запутывающих особенностей языка JavaScript. Пришедшее из Java, оно было призвано помочь в реализации ООП. Я какое-то время писал на Java, и должен сказать, что за это время у меня, может быть, один раз возникло сомнение, чему равняется this в конкретном месте кода. В JavaScript такие сомнения могут возникать каждый день — по крайней мере, до того момента, как выучишь несколько простых, но неочевидных правил.

Заблуждения о this

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

this — это лексический контекст.

Такое впечатление часто возникает у новичков. Им кажется, что this — это объект, в котором, как свойства, хранятся все переменные в данной области видимости. Это заблуждение происходит из того, что в одном конкретном случае это, грубо говоря, так. Если мы находимся на самом верхнем уровне, то this равняется window (в случае обычного скрипта в браузере). А как мы знаем, все переменные, объявленные на верхнем уровне, доступны как свойства window .

В общем случае это неправда. Это легко проверить.

function test() < const a = "Кто прочитал этот текст в консоли, тот скоро умрёт"; console.log(this.a); >test(); // не волнуйтесь, никто не умрёт 

this — это объект, которому принадлежит метод

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

const f = function() < console.log(this); >const object1 = < method: f >; const object2 = < method: f >; 

Так какой же из этих объектов будет её this’ом?

Важно! Даже если объект создан с помощью классов ES6, это совершенно не гарантирует, что у его метода всегда будет нужный this . Пусть вас не вводит в заблуждение сходство с классами из «нормальных» языков.

this — это джедайская техника, которую, изучив, нужно использовать везде

По возможности лучше избегать употребления this . Единственный случай, когда оно полностью обосновано — это ООП. В прочих местах употребление this — это не то чтобы зло, но, как правило, излишество, запутывающее код. По сути this — это дополнительный, «минус первый» аргумент функции, который передаётся туда сложным, неочевидным новичку путём. С высокой вероятностью его можно заместить обычным аргументом, и станет только лучше.

Как определить значение this

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

    Мы находимся внутри функции?

  • Да: смотрим следующий пункт.
  • Нет: this равен глобальному объекту.

Комментарий

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

  • Да: значение this такое же, как и в функции на уровень выше (т.е. содержащей данную). Вернитесь на предыдущий шаг и повторите алгоритм для неё. Если же функция не содержится ни в какой другой, this — глобальный объект.
  • Нет: смотрим следующий пункт.

Комментарий

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

  • Да: this ссылается на новый объект, находящийся «в процессе конструкции».
  • Нет: смотрим следующий пункт.

Комментарий

Пожалуй, стоит отдельно оговорить случай, когда речь идёт о конструкторе унаследованного ES6-класса. Тогда до вызова super() значения у this нет (обращение к нему вызовет ошибку), а после вызова super() он равняется новому объекту родительского класса.

  • Да: значение this равняется значению первого аргумента, который мы передали в метод bind при создании данной функции.
  • Нет: смотрим следующий пункт.

Комментарий

Метод bind создаёт копию функции, зафиксировав для неё this и, опционально, несколько первых аргументов. На самом деле при этом создаётся не просто копия функции, а, цитирую, «экзотический объект BoundFunction». Экзотичность его проявляется, в частности, в том, что повторным вызовом bind мы уже не сможем изменить this . Поэтому, строго говоря, ответ в этом пункте надо было сформулировать так: если да, то this равняется первому аргументу первого вызова bind , который привёл к созданию данной функции.

  • Да: одному Господу известно, чему будет равен this при её вызове. Идите читать документацию по той штуке, которая её станет вызывать.
  • Нет: смотрим следующий пункт.

Комментарий

У не стрелочной и не связанной (bound) функции значение this зависит от обстоятельств, в которых она была вызвана. Если вы не вызываете её лично, а передаёте куда-то, то в качестве this может быть или не быть подставлено неизвестное вам значение.

`use strict` document.addEventListener('keydown', function()< console.log(this); >); // в этом случае this === document. при срабатывании обработчиков DOM-событий this равняется currentTarget [1, 2, 3].forEach(function()< console.log(this); >); // а в этом случае никакого специального значения не будет, будет undefined. почему? об этом в самом конце. 
  • Да: в таком случае this равняется первому аргументу, переданному соответствующему методу.
  • Нет: смотрим следующий пункт.

Комментарий

Ещё один способ явно задать this . Точнее, два. Однако в плане this разницы между apply и call нет никакой, разница только в том, как передаются остальные аргументы.

  • Да: this равняется вышеупомянутому объекту.
  • Нет: смотрим следующий пункт.

Комментарий

Собственно, из этого механизма (а также — из опыта работы с другими языками) растут ноги у убеждения, что » this — это объект, чей метод мы вызвали». Пожалуй, я просто напишу код.

'use strict'; const object1 = < method: function()< console.log(this); >> const object2 = < method: object1.method >object1.method(); // в консоли будет object1 - мы получили функцию как свойство этого объекта и немедленно вызвали object1['method'](); // аналогичный результат. этот механизм не специфичен для точечной нотации object2.method(); // в консоли будет object2 - метод "не помнит", в каком объекте он был создан, ему важно только у какого объекта он вызван 
  • Да: this равняется undefined .
  • Нет: this равен глобальному объекту.

Комментарий

Если мы дошли до этого пункта, значит, this не задан ни одним из механизмов, которые позволяют его задать. Существуют различные заблуждения относительно того, как ещё может передаваться this . Например, на собеседованиях мне часто говорят вот такую вещь:

const obj = < test: function()< (function()< console.log(this); >)(); //немедленно вызываемая функция внутри другой функции > > obj.test(); // мне говорят, что в консоль выведется obj. это неправда 

Или, как я уже говорил в секции «заблуждения», многие считают, что если функция является методом объекта, созданного с помощью классов ES6, то уж в ней-то this всегда будет равен этому объекту. Это тоже неправда.

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

Исторически в качестве «дефолтного» this в такие функции передавался глобальный объект. Позже этот подход был признан небезопасным. В ES5 появился строгий режим, исправляющий многие проблемы более ранних версий ECMAScript. Он включается директивой ‘use strict’ в начале файла или функции. В таком режиме «дефолтное» значение this — это undefined .

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

Также существуют другие механизмы включения строгого режима, например, в NodeJS строгий режим для всех файлов можно включить флагом —use-strict .

P.S. Пользователь Aingis подсказал, что в случае использования конструкции with объект, переданный в неё в качестве контекста, подменяет собой глобальный объект. Пожалуй, я не стану вносить это в свой классификатор, потому что шанс встретить with в 2019+ году довольно мал. Но в любом случае это интересный момент.

P.P.S. Пользователь rqrqrqrq подсказал, что у new выше приоритет, чем у bind . Соответствующая правка в классификатор уже внесена. Спасибо!

this

Поведение ключевого слова this в JavaScript несколько отличается по сравнению с остальными языками. Имеются также различия при использовании this в строгом и нестрогом режиме.

В большинстве случаев значение this определяется тем, каким образом вызвана функция. Значение this не может быть установлено путём присваивания во время исполнения кода и может иметь разное значение при каждом вызове функции. В ES5 представлен метод bind() , который используется для привязки значения ключевого слова this независимо от того, как вызвана функция . Также в ES2015 представлены стрелочные функции , которые не создают собственные привязки к this (они сохраняют значение this лексического окружения, в котором были созданы).

Интерактивный пример

Синтаксис

this

Значение

Свойство контекста выполнения кода (global, function или eval), которое в нестрогом режиме всегда является ссылкой на объект, а в строгом режиме может иметь любое значение.

Global контекст

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

// В браузерах, объект window также является объектом global: console.log(this === window); // true a = 37; console.log(window.a); // 37 this.b = "MDN"; console.log(window.b); // "MDN" console.log(b); // "MDN" 

Примечание: вы всегда можете легко получить объект global, используя глобальное свойство globalThis , независимо от текущего контекста, в котором выполняется ваш код.

Function контекст

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

Простой вызов

Поскольку следующий код не в строгом режиме , и значение this не устанавливается вызовом, по умолчанию будет использоваться объект global, которым в браузере является window .

function f1()  return this; > // В браузере: f1() === window; // window - глобальный объект в браузере // В Node: f1() === global; // global - глобальный объект в Node 

В строгом режиме, если значение this не установлено в контексте выполнения, оно остаётся undefined , как показано в следующем примере:

function f2()  "use strict"; // см. strict mode return this; > f2() === undefined; // true 

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

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

Пример 1

// В качестве первого аргумента методов call или apply может быть передан объект, // на который будет указывать this. var obj =  a: "Custom" >; // Это свойство принадлежит глобальному объекту var a = "Global"; function whatsThis()  return this.a; //значение this зависит от контекста вызова функции > whatsThis(); // 'Global' whatsThis.call(obj); // 'Custom' whatsThis.apply(obj); // 'Custom' 

Пример 2

function add(c, d)  return this.a + this.b + c + d; > var o =  a: 1, b: 3 >; // Первый параметр - это объект для использования в качестве // 'this', последующие параметры передаются как // аргументы функции call add.call(o, 5, 7); // 16 // Первый параметр - это объект для использования в качестве // 'this', второй - массив, чьи члены используются // в качестве аргументов функции call add.apply(o, [10, 20]); // 34 

Обратите внимание, что в нестрогом режиме, если значение, переданное в call или apply как this , не является объектом, будет сделана попытка преобразовать его в объект с помощью внутренней операции ToObject . Таким образом, если переданное значение является примитивом, таким как 7 или ‘foo’ , оно будет преобразовано в Object с использованием связанного конструктора, так что примитивное число 7 будет преобразовано в объект так, как будто с помощью new Number(7) , а строка ‘foo’ — как будто с помощью new String(‘foo’) , например

function bar()  console.log(Object.prototype.toString.call(this)); > bar.call(7); // [object Number] bar.call("foo"); // [object String] 

Метод bind

ECMAScript 5 представил Function.prototype.bind() . Вызов f.bind(someObject) создаёт новую функцию с тем же телом и областью действия, что и f , но там, где в исходной функции используется this , в новой функции оно постоянно будет связано с первым аргументом bind , независимо от того, как функция используется.

function f()  return this.a; > var g = f.bind( a: "azerty" >); console.log(g()); // azerty var h = g.bind( a: "yoo" >); // bind only works once! console.log(h()); // azerty var o =  a: 37, f: f, g: g, h: h >; console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty 

Стрелочные функции

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

var globalObject = this; var foo = () => this; console.log(foo() === globalObject); // true 

Примечание: Note: если аргумент this передаётся в call, bind или apply при вызове стрелочной функции, он будет проигнорирован. Вы всё ещё можете добавить аргументы к вызову, но первый аргумент (thisArg) должен быть установлен в null.

Неважно, как стрелочная функция foo() будет вызвана, её значение this будет указывать на глобальный объект. this будет сохранять своё значение, даже если функция foo() будет вызвана как метод объекта (что в обычных функциях связывает this с объектом вызова) или с использованием методов call , apply или bind :

// Вызов функции как метода объекта var obj =  foo: foo >; console.log(obj.foo() === globalObject); // true // Попытка установить this с помощью call console.log(foo.call(obj) === globalObject); // true // Попытка установить this с помощью bind foo = foo.bind(obj); console.log(foo() === globalObject); // true 

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

// Создаём объект obj, содержащий метод bar, который возвращает функцию, // которая возвращает свой this. Возвращаемая функция создана // как стрелочная функция, таким образом её this постоянно замкнут // на this функции, в которой она создана. Значение bar может быть установлено // в вызове, который, в свою очередь, устанавливает значение возвращаемой функции. var obj =  bar: function ()  var x = () => this; return x; >, >; // Вызываем bar как метод объекта obj, устанавливая его this на obj // Присваиваем ссылку возвращаемой функции переменной fn var fn = obj.bar(); // Вызываем fn без установки this, что в обычных функциях указывало бы // на глобальный объект или undefined в строгом режиме. console.log(fn() === obj); // true // Но будьте осторожны, если вы ссылаетесь на метод obj, не вызывая его var fn2 = obj.bar; // Вызов this стрелочной функции изнутри метода bar вернёт теперь window, // потому что он следует за this из fn2. console.log(fn2()() == window); // true 

В примере выше, функция (назовём её анонимной функцией A), присвоенная методу obj.bar , возвращает другую функцию (назовём её анонимной функцией B) которая создана как стрелочная функция. В результате, this функции B при вызове замкнут на this, принадлежащий obj.bar (функции A). this функции B всегда будет иметь то значение, которое он получил при создании. В примере выше, this функции B указывает на this функции A,которым является obj, таким образом this будет равен obj даже тогда, когда будет вызван методом, который в нормальных условиях устанавливал бы значение this равным undefined или глобальному объекту (или любым другим методом, как в предыдущем примере в глобальном контексте выполнения).

В методе объекта

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

В следующем примере, когда вызвано свойство o.f() , внутри функции this привязано к объекту o.

var o =  prop: 37, f: function ()  return this.prop; >, >; console.log(o.f()); // logs 37 

Необходимо отметить, что на поведение this совсем не влияет то, как или где была определена функция. В предыдущем примере мы определили функцию внутри свойства f во время определения объекта o . Однако, мы могли бы также просто определить сначала функцию, а затем закрепить её за свойством o.f . В этом случае поведение this не изменится:

var o =  prop: 37 >; function independent()  return this.prop; > o.f = independent; console.log(o.f()); // logs 37 

Эти примеры показывают, что имеет значение только то, что функция была вызвана из свойства f объекта o .

Аналогично, привязывание this обуславливается наличием ближайшей ссылки на объект или свойство. В следующем примере, когда мы вызываем функцию, мы обращаемся к ней как к методу g объекта o.b . На этот раз во время выполнения, this , что находится внутри функции, будет ссылаться на o.b . Тот факт, что объект является членом объекта o , не имеет значения; важна только ближайшая ссылка.

.b =  g: independent, prop: 42 >; console.log(o.b.g()); // logs 42 
this в цепочке object’s prototype

Это же представление справедливо и для методов, определённых где-либо в цепочке object’s prototype. Если метод находится в цепочке прототипов, то this ссылается на объект, на котором был вызван метод, т.е. так, словно метод является методом самого объекта, а не прототипа.

var o =  f: function ()  return this.a + this.b; >, >; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5 

В этом примере объект, который присвоен переменной p , не имеет собственного свойства f , а наследует это свойство от своего прототипа. Однако, совершенно неважно, что поиск свойства f в конце концов обнаружит его на объекте o . Поскольку поиск начался с p.f , то и свойство this внутри функции f будет ссылаться на объект p . Таким образом, если f вызывается как метод p , то и this относится к p . Это полезная особенность прототипного наследования JS.

this с геттерами/сеттерами

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

function modulus()  return Math.sqrt(this.re * this.re + this.im * this.im); > var o =  re: 1, im: -1, get phase()  return Math.atan2(this.im, this.re); >, >; Object.defineProperty(o, "modulus",  get: modulus, enumerable: true, configurable: true, >); console.log(o.phase, o.modulus); // logs -0.78 1.4142 

В конструкторе

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

Примечание: по умолчанию конструктор возвращает объект, на который ссылается this , но он может вернуть и другой объект (если возвращаемое значение не является объектом, тогда будет возвращён объект с this ).

/* * Конструктор работает таким образом: * * function MyConstructor() < * // фактический код, составляющий тело функции. * // создание свойств с |this| по * // желанию, определяя их значения; например, * this.fum = "nom"; * // и т.д. * * // Если функция возвращает выражение, * // возвращающее объект, этот объект будет * // результатом выражения |new|. В обратном случае, * // результат выражения - объект, * // в данный момент привязанный к |this| * // (т.е. наиболее часто встречающийся случай). * >*/ function C()  this.a = 37; > var o = new C(); console.log(o.a); // logs 37 function C2()  this.a = 37; return  a: 38 >; > o = new C2(); console.log(o.a); // logs 38 

В последнем примере ( C2 ), из-за того, что конструктор вернул объект, новый объект, к которому было привязано this , был просто отброшен. (Это фактически делает выражение » this.a = 37; » «мёртвым» кодом. Он не является буквально нерабочим, так как он выполняется, но он может быть изъят без каких-либо внешних эффектов.)

call и apply

Когда в теле функции используется ключевое слово this , его значение может быть привязано к конкретному объекту в вызове при помощи методов call или apply , которые наследуются всеми функциями от Function.prototype .

function add(c, d)  return this.a + this.b + c + d; > var o =  a: 1, b: 3 >; // Первый параметр - это объект, который следует использовать как // 'this', последующие параметры передаются // как аргументы при вызове функции add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // Первый параметр - объект, который следует использовать как // 'this', второй параметр - массив, // элементы которого используются как аргументы при вызове функции add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 

Необходимо отметить, что если методам call и apply передаётся значение с this , которое не является при этом объектом, будет предпринята попытка конвертировать значение в объект, используя внутреннюю операцию ToObject . Если переданное значение является примитивным типом, например 7 или ‘foo’ , оно будет преобразовано в объект с использованием родственного конструктора, так примитив 7 преобразовывается в объект через new Number(7), а строка ‘foo’ в объект через new String(‘foo’), и т.д.

function bar()  console.log(Object.prototype.toString.call(this)); > bar.call(7); // [object Number] 

Как обработчик событий DOM

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

// Когда вызывается как обработчик, связанный элемент становится синим function bluify(e)  // Всегда true console.log(this === e.currentTarget); // true, когда currentTarget и target один объект console.log(this === e.target); this.style.backgroundColor = "#A5D9F3"; > // Получить список каждого элемента в документе var elements = document.getElementsByTagName("*"); // Добавить bluify как обработчика кликов, чтобы при // нажатии на элемент он становился синим for (var i = 0; i  elements.length; i++)  elements[i].addEventListener("click", bluify, false); > 

В инлайновом обработчике событий

Когда код вызван из инлайнового обработчика, this указывает на DOM-элемент, в котором расположен код события:

button onclick="alert(this.tagName.toLowerCase());">Показать this/button> 

Код выше выведет ‘ button ‘. Следует отметить, что this будет указывать на DOM-элемент только во внешних (не вложенных) функциях:

button onclick="alert((function() ()));"> Показать вложенный this /button> 

В этом случае this вложенной функции не будет установлен, так что будет возвращён global/window объект.

Спецификации

Specification
ECMAScript Language Specification
# sec-this-keyword

Совместимость

BCD tables only load in the browser

Смотрите также

  • Строгий режим
  • All this, статья о this в разном контексте
  • Краткое объяснение ключевого слова ‘this’ в JavaScript

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 7 авг. 2023 г. by MDN contributors.

Your blueprint for a better internet.

MDN

Support

  • Product help
  • Report an issue

Our communities

Developers

  • Web Technologies
  • Learn Web Development
  • MDN Plus
  • Hacks Blog
  • Website Privacy Notice
  • Cookies
  • Legal
  • Community Participation Guidelines

Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.

Что такое this

Пусть задано следующее определение функции:

 и два объекта: var rect = < x:10, y:20, square:sq >; var cube = < x:2, y:3, z:16, square:sq >; 

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

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

 >; var cube = < x:2, y:3, z:16, square:rect.square >; rect.square() // Равно 200 // Здесь this относится к rect cube.square() // Равно 6 // Здесь this относится к cube, хотя функция // определена в rect 

Видим, насколько JavaScript является контекстно-зависимым языком. В других объектно-ориентированных языках методы жёстко привязаны к объектам, внутри которых они заданы, и не могут работать с другими объектами.

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

Какой результат будет получен при запуске в котором нет явной связи с каким либо объектом? Проверим:

Дело вот в чём. Функции в JavaScript всегда работают в контексте какого либо объекта. Если объект явно не задан, то таким объектом выступает глобальный объект window (окно браузера), если JavaScript работает в браузере.

Значит, this относится именно к window и x и y — переменные этого глобального объекта. Мы эти переменные не вводили. Но в JavaScript есть правило: если переменная не описана явно при помощи инструкции var , она считается глобальной и автоматически описывается (создаётся) в глобальном объекте window .

Таким образом, результат вызова sq() объясняется следующим образом.

  1. Создаются глобальные переменные с именами x и y .
  2. Результат умножения x*y равен NaN , так как значения переменных равны undefined ( undefined*undefined есть NaN )

А вот в таком коде будет получен «нормальный» числовой результат:

 var x=7; var y=8; var z = sq(); // Равно 56 

О глобальном объекте window , в контексте которого всегда работает код на JavaScript, и о глобальных переменных, которые на самом деле являются свойствами объекта window , мы поговорим подробнее, когда будем присматриваться к областям видимости переменных и обсуждать работу функций (заметки 5, 7, 8 и 9).

Подробно о том, как работает this в JavaScript

Понимание this в JavaScript и того, что имеется ввиду при его упоминании, может быть немного затруднительным. Но к счастью, есть пять главных правил, которые вы можете использовать для определения того, к чему привязан this . Есть несколько кейсов, которые эти правила не затрагивают, НО они должны помочь вам с подавляющим большинством рабочих ситуаций. Давайте начнем.

��Мой Твиттер — там много из мира фронтенда, да и вообще поговорим��. Подписывайтесь, будет интересно: ) ✈️

Значение this обычно определено контекстом выполняемой функции. Под контекстом подразумевается то, как вызывается функция.

Очень важно знать, что this может быть разным (т.е. чем-то совсем другим), каждый раз при вызове функции.

Вполне ок, если первые два пункта не несут смысла для вас. Под конец все будет понятно.

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

Итак, уберем определения в сторону и немного отвлечемся. Откройте вашу Chrome Developer Console ( Windows: Ctrl + Shift + J )( Mac: Cmd + Option + J ) и напечатайте там следующее:

console.log(this);

Что вы получите?

// Window

Объект window ! Это произойдет из-за глобальной области видимости, this будет относится к глобальному объекту.

Чтобы разобраться, почему this относится к объекту window , давайте посмотрим на это более детально. В вашей консоли создайте новую переменную и назначьте ей имя:

var myName = 'Brandon';

Мы можем получить доступ к этой переменной, просто вызвав её:

myName// returns -> 'Brandon'

Но вы знали, что каждая переменная, которую вы объявляете в глобальной области видимости прикреплена к объекту window ? Давайте протестируем это:

window.myName
// returns -> 'Brandon'
window.myName === myName
// returns -> true

Круто. До этого, когда мы запустили console.log(this) в глобальном контексте, мы знали, что this было запущено на глобальном объекте. А так как глобальный объект в браузере это объект window , то имеет смысл быть:

console.log(this)
// returns -> window

Ок, тут всё понятно. А теперь давайте вставим this в функцию. Вспомним наше старое определение: значение this обычно определяется тем, как функция вызывается. Учитывая это, что вы ожидаете будет в том, что вернет эта функция? В консоле браузера, скопируйте код ниже и нажмите на enter.

function test() return this;
>
test()

И снова, this возвращает глобальный объект. Это происходит из-за того, что this не внутри объявленного объекта, так что по-умолчанию оно относится к глобальному объекту. Такую концепцию может быть немного сложно понять, хотя бы прямо сейчас, но это обретёт очертания смысла далее, по мере прочтения статьи. Кое что, что нужно подметить, если вы используете strict mode , это то, что в примере выше this будет undefined .

Объявленный объект

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

var person = first: 'John', 
last: 'Smith',
full: function() console.log(this.first + ' ' + this.last);
>
>;
person.full();
// logs => 'John Smith'

Чтобы лучше показать, что this по факту относится к объекту person , скопируйте код ниже в консоль браузера. Это по большинству тот же код, что и выше, только мы будем использовать console.log(this) и увидим, что нам вернется.

var person = first: 'John', 
last: 'Smith',
full: function() console.log(this);
>
>;
person.full();
// logs => Object

Как вы увидите, консоль возвращает объект person , доказывая то, что this взяло значение person .

Одна важная деталька перед тем как идти дальше. Помните, что мы говорили о том, что значение this выставляется в соответствии с ближайшим родительским объектом, который вызывает метод? Что вы думаете произойдет, если мы столкнемся с вложенными объектами? Давайте посмотрим на пример кода ниже. У нас есть person объект, включающий в себя ключи first , last и full , в общем все как раньше. Но в этот раз, мы также вложили объект personTwo . Этот объект состоит из тех же трех ключей.

var person = first: 'John', 
last: 'Smith',
full: function() console.log(this.first + ' ' + this.last);
>,
personTwo: first: 'Allison',
last: 'Jones',
full: function() console.log(this.first + ' ' + this.last);
>
>
>;

Что случится, если мы вызовем два ниших метода full . Давайте узнаем.

person.full();
// logs => 'John Smith'
person.personTwo.full();
// logs => 'Allison Jones'

Снова, значение this равняется ближайшему родительскому объекту, который вызывает метод. Когда person.full() вызывается внутри функции, this связывается с объектом person . В это время, когда person.personTwo.full() вызывается внутри функции full , this связывается с объектом personTwo .

С использованием new

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

function Car(make, model) this.make = make; 
this.model = model;
>;

Вы можете предположить, что this привязан к глобальному объекту и вы будете правы, но до тех пор пока мы не добавим new . При его использовании, значение this выставляется пустому объекту, как в этом случае, myCar .

var myCar = new Car('Ford', 'Escape');console.log(myCar);
// logs => Car

Для того, чтобы это имело смысл, вам надо понять, что на самом деле делает new . Это по факту отдельная тема. Так что если вы неуверенны и вы видите ключевик new , то просто знайте, что в этом случае this относится к совершенно новому, пустому объекту.

Call, Bind, Apply

Про эти методы можно прочитать тут:

Последнее, но конечно же не менее важное. Мы можем явно указывать значение this с помощью call() , bind() и apply() . Все три очень похожи, но очень важно понимать незначительные различия.

Call и Apply , каждый является функцией быстрого вызова. Call берёт любое число параметров и также может принимать дополнительные аргументы. Apply берет только два параметра: this и массив дополнительных аргументов.

Вы всё ещё читаете? Тогда пример ниже должен внести ясности. Давайте посмотрим. Мы попытаемся сложить числа. Скопируйте этот код в вашу консоль и вызовите функцию.

function add(c, d) console.log(this.a + this.b + c + d);
>
add(3,4);
// logs => NaN

Функция add выдаёт NaN . Это происходит из-за того, что this.a и this.b это undefined . Их просто не существует. И вы не можете сложить числа с undefined .

Давайте добавим объект к уравнению. Мы можем использовать call() и apply() для вызова функции с нашим объектом:

function add(c, d) console.log(this.a + this.b + c + d);
>
var ten = ;add.call(ten, 3, 4);
// logs => 10
add.apply(ten, [3,4]);
// logs => 10

Когда мы используем add.call() , то первый параметр это то, чем будет this . Последующие параметры передаются функции, которую мы вызываем. Следовательно, в add() , this.a соответствует ten.a и this.b соответствует ten.b и мы получаем на выходе 1+2+3+4 или 10 .

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

Что по поводу bind ? Параметры в bind() идентичны call() , но bind() не вызывается сразу же. Вместо этого, bind() возвращает функцию с контекстом this , который уже связан. Из-за этого bind() полезен тогда, когда мы не знаем все аргументы наперед. Снова, пример должен вам помочь в понимании:

var small = a: 1, 
go: function(b,c,d) console.log(this.a+b+c+d);
>
>
var large = a: 100
>

Скопируйте текст выше в консоль. И затем вызовите:

small.go(2,3,4);
// logs 1+2+3+4 => 10

Круто. Ничего нового тут нет. Но что, если нам нужно использовать значение large.a ? Мы можем использовать call / apply :

small.go.call(large,2,3,4);// logs 100+2+3+4 => 109

Итак, а если мы не знаем все три аргумента? Тогда мы можем использовать bind :

var bindTest = small.go.bind(large,2);

Если мы выведем в консоль нашу переменную выше, то мы увидим с чем имеет дело:

console.log(bindTest);
// logs => function (b,c,d)

Помните, что с bind , функция возвратила, то что уже привязано к this . Так что наш this был успешно привязан к объекту large . Мы также передали наш второй аргумент, как число 2 . Позже, когда известны другие аргументы, мы можем передать их:

bindTest(3,4);// logs 100+2+3+4 => 109

Для ясности. Вот весь код в одном блоке. Просмотрите его и скопируйте в консоль, чтобы понять то, что действительно происходит!

var small = a: 1, 
go: function(b,c,d) console.log(this.a+b+c+d);
>
>
var large = a: 100
>
small.go(2,3,4);
// logs 1+2+3+4 => 10
var bindTest = small.go.bind(large,2);console.log(bindTest);
// logs => function (b,c,d)
bindTest(3,4);
// logs 100+2+3+4 => 109

Заключение

Вы сделали это! В большинстве случаев теперь вы сможете определить, что означает this . Но запомните несколько вещей:

Значение this обычно определяется контекстом выражения функции.

В глобальном поле видимости this относится к глобальному объекту.

При использовании new , this привязывает к новому созданному объекту.

Мы можем явно указывать значения this , с помощью call() , bind() и apply() .

Стрелочные функции не привязывают this — заместо этого this связывается лексически, основываясь на оригинальном контексте.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *