Потеря контекста вызова в JavaScript | Курс ReactJS в Черкассах

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

Вопрос:

Объясните, пожалуйста, почему после присвоенияvar f = obj1.fтеряется контекст вызова и выводитсяundefined?

var obj1 = {

  x: 3,

  f: function() {
    return (this.x);
  }
};

alert(obj1.f());
var f = obj1.f;
alert(f());

Онлайн курс ReactJS в Черкассах | ReactWarriors

Ответ:

Значение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в них кажется неожиданным.

Вызов метода

Метод - это функция находящаяся в объекте

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();

Онлайн курс ReactJS в Черкассах | ReactWarriors

Вызов конструктора

Функцию можно вызывать в качестве конструктора, для этого перед вызовом нужно использовать оператор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для передаваемого коллбэка:

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');

Онлайн курс ReactJS в Черкассах | ReactWarriors

Поделись знаниями: