Объясните, пожалуйста, почему после присвоения var f = obj1.f теряется контекст вызова и выводится undefined?
var obj1 = {
x: 3,
f: function() {
return (this.x);
}
};
alert(obj1.f());
var f = obj1.f;
alert(f());
Ответ
Значение this внутри функции зависит от того как вызывается функция и как создана функция.
Как вызывается?
Вызвать функцию можно следующими способами:
Вызов функции
Если есть обычная функция, в большинстве случаев значением this будет глобальный объект (для браузера window). При использовании "strict mode" - undefined.
var f = function (){
console.log('common:',this.toString());
};
f();
var fStrict = function (){
"use strict";
console.log('strict:', this);
};
fStrict();
Обычно так вызываются функции обратного вызова(callback), вот почему значение this в них кажется неожиданным.
Например, при передаче функции в .addEventListener, значением this будет элемент, которому добавлен обработчик.
Вызов метода
Метод - это функция находящаяся в объекте.
var Obj = {toString:function(){ return "[object Obj]";}};
Obj.f = function (){
console.log('common:',this.toString());
};
Obj.f();
Obj.fStrict = function (){
"use strict";
console.log('strict:', this.toString());
};
Obj.fStrict();
Когда функция вызывается как метод, значением this является объект в котором находится функция, фактически значение перед символом точки.
Вызов конструктора
Функцию можно вызывать в качестве конструктора, для этого перед вызовом нужно использовать оператор new: new Foo()
function Foo(name){
this.name = name;
}
var foo = new Foo('foo');
console.log(foo);
При вызове функции в качестве конструктора создается новый объект, и значение this ссылается на это созданный объект.
Особенность: при использовании наследования и классов из ES2015 обращение к this до вызова super в зависимости от браузера вызовет исключение о попытке обратиться к необъявленной/неинициализированной переменной.
class A {}
class B extends A {
constructor(){
console.log(this);
}
}
var b = new B();
Вызов с помощью методов call и apply
При использовании функций call и apply можно задать значение this напрямую, передав его первым параметром.
var f = function (){
console.log('common:',this);
};
f.call({o:'object'});
var fStrict = function (){
"use strict";
console.log('strict:', this);
};
fStrict.apply({o:'object'});
В библиотеках вроде jQuery с помощью этих функций вызываются коллбэки передаваемые в различные функции, например: each, map, on и другие. В качестве this в этом случае устанавливается текущий элемент коллекции, либо html-элемент.
Вызов в качестве коллбэков в функциях обработки массивов
Некоторые встроенные функции для объекта типа Array позволяют так же напрямую указать значение this для передаваемого коллбэка:
- Array.every
- Array.filter
- Array.find
- Array.findIndex
- Array.forEach
- Array.map
- Array.some
var specialMap = {'b':'specialB','d':'specialD'}
var source= ['a','b','c','d','e'];
var mapped = source.map(function(el){
return this[el] || ('common-'+el);
},specialMap);
console.log('source:',source);
console.log('mapped:',mapped);
Как создается?
Объявление функции или функционального выражения
Обычное объявление функции:
function A(){}
var a = function (){};
при обычном объявлении значение this определяется при вызове способами описанными выше.
Создание функции с помощью bind
Функция bind возвращает новую привязанную функцию. Значение this внутри созданной функции всегда то, которое передали при вызове bind.
Важная особенность: при использовании привязанной функции в качестве конструктора, значение this все равно будет указывать на создаваемый объект, как описано выше.
Важная особенность: в НЕ strict mode при передаче в качестве параметра this значений null и undefined - этот параметр будет проигнорирован и this будет установлен в глобальный объект.
function A(){console.log(typeof this,'is window', this === window);}
console.log('execute with null');
A.bind(null)();
console.log('execute with undefined');
A.bind(undefined)();
function A1(){'use strict'; console.log(typeof this, this);}
console.log('execute with null');
A1.bind(null)();
console.log('execute with undefined');
A1.bind(undefined)();
Важная особенность: значение this у созданной функции нельзя переопределить используя функции call и apply описанные выше.
function A(){console.log(this);}
var B = A.bind({o:'object'});
console.log('execute binded');
B();
console.log('execute with call');
B.call({another: 'some new object'});
console.log('execute as constructor');
new B();
Стрелочные функции
Стрелочные функции появились в ES2015 и при создании привязываются к текущему значению this.
После создания значение this нельзя поменять указанными выше способами.
Кроме того стрелочную функцию нельзя использовать в качестве конструктора.
function A(){
this.t = (place)=>console.log(place,this);
}
var a = new A()
a.t('method:');
var tt = a.t;
tt('free function execute:');
tt.call({o:'object'},'using call function');
new tt('constructor');