Потеря контекста вызова

1,00
р.
Объясните, пожалуйста, почему после присвоения 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')


на основе ответов: - How does the “this” keyword work? - How does “this” keyword work within a JavaScript object literal?