Эта статья нуждается в редакционном обзоре. Как вы можете помочь.
Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.
Сводка
Поведение ключевого слова this
в JavaScript несколько отличается по сравнению с остальными языками. Имеются также различия при использовании this
в строгом и нестрогом режиме.
В большинстве случаев значение this
определяется тем, каким образом вызвана функция. Значение this
не может быть установлено путем присваивания во время исполнения кода и может иметь разное значение при каждом вызове функции. В ES5 представлен метод bind
, чтобы определить значение ключевого слова this независимо от того, как вызвана функция. Также в ECMAScript 2015 представлены стрелочные функции, this которых привязан к окружению, в котором была создана стрелочная функция.
Синтаксис
this
Глобальный контекст
В глобальном контексте выполнения (за пределами каких-либо функций), this
ссылается на глобальный объект вне зависимости от использования в строгом или нестрогом режиме.
console.log(this.document === document); // true // В браузерах, объект window также является глобальным: console.log(this === window); // true this.a = 37; console.log(window.a); // 37
В контексте функции
В пределах функции значение this
зависит от того, каким образом вызвана функция.
Простой вызов
function f1(){ return this; } f1() === window; // global object
В этом случае значение this
не устанавливается вызовом. Так как этот код написан не в строгом режиме, значением this
всегда должен быть объект, по умолчанию - глобальный объект.
function f2(){ "use strict"; // see strict mode return this; } f2() === undefined;
В строгом режиме, значение this
остается тем значением, которое было установлено в контексте исполнения. Если такое значение не определено, оно остается undefined. Оно может быть установлено любым значением: null
или 42
или "Я не this"
.
this
должно иметь значение undefined
, потому что функция f2
была вызвана без ссылки на какую-либо основу (например, window.f2()
). Реализация этой особенности не поддерживалась в некоторых браузерах, в то время когда они уже начали поддерживать строгий режим. В результате они некорректно возвращали объект window
.Стрелочные функции
В стрелочных функциях, this
привязан к окружению, в котором была создана функция. В глобальной области видимости, this
будет указывать на глобальный объект.
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
Не важно, как функция 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 функции в которой она созданна.
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
В примере выше, функция (назовем ее анонимной функцией A) присвоенная obj.bar
возвращает другую функцию (назовем ее анонимной функцией B) которая созданна как стрелочная функция. В результате вызова функции A, 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
не имеет значения; важна только ближайшая ссылка.
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
or 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]
Метод 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 o = {a:37, f:f, g:g}; console.log(o.f(), o.g()); // 37, azerty
Как обработчик событий DOM
Когда функция используется как обработчик событий, this
присваивается элементу с которого начинается событие (некоторые браузеры не следуют этому соглашению для слушателей добавленных динамически с помощью всех методов кроме addEventListener
).
// When called as a listener, turns the related element blue function bluify(e){ // Always true console.log(this === e.currentTarget); // true when currentTarget and target are the same object console.log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // Get a list of every element in the document var elements = document.getElementsByTagName('*'); // Add bluify as a click listener so when the // element is clicked on, it turns blue 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(){return this}()));"> Показать вложенный this </button>
В этом случае this
вложеной функции не будет установлен, так что будет возвращен global/window объект.
Спецификации
Specification | Status | Comment |
---|---|---|
ECMAScript 1st Edition. | Standard | Initial definition. Implemented in JavaScript 1.0 |
ECMAScript 5.1 (ECMA-262) Определение 'The this keyword' в этой спецификации. |
Стандарт | |
ECMAScript 2015 (6th Edition, ECMA-262) Определение 'The this keyword' в этой спецификации. |
Стандарт |
Browser compatibility
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | (Да) | (Да) | (Да) | (Да) | (Да) |
Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Basic support | (Да) | (Да) | (Да) | (Да) | (Да) | (Да) |
Смотри также
- Строгий режим
- All this, an article about
this
in different contexts