03. Function Invocation
In JavaScript, this is the current execution context of a function. JS has 5 function invocation types:
Function invocation:
alert('Hello World!')Method invocation:
console.log('Hello World!')Arrow function invocation.
Constructor invocation:
new RegExp('\\d')Indirect invocation:
alert.call(undefined, 'Hello World!')and each one defines its own context, this behaves slight different than developer expects.
Before starting, let's familiarize with a couple of terms:
Invocation is executing the code that makes the body of a function (simply calling the function). For example
parseIntfunction invocation isparseInt('15').Context of an invocation is the value of
thiswithin function body.Scope of a function is a set of variables, objects, functions accessible within a function body.
Function Invocation
In function invocation this is the global object. The global object is determined by the execution environment.
It is the
windowobject in a web browser.It is the
globalobject in a node environment.thisisundefinedin a function invocation in strict mode. The strict mode is active not only in the current scope, but also in the inner scopes (for all functions declared inside).
function func() {
return this;
}
const val = func();
val === this; // true
// in browser
val === window; // true
// in node
val === global; // trueA common trap with the function invocation is thinking that this is the same in an inner function as in the outer function. Correctly the context of the inner function depends only on invocation, but not on the outer function's context. To have the expected this, modify the inner function's context with indirect invocation (using .call() or .apply()) or create a bound function (using .bind()).
this для вложенной функции определяется самой этой функцией, только если она не стрелочная.
Method Invocation
Method invocation is performed when in a form of property accessor that evaluates to a function object. this is the object that owns the method in a method invocation.
const calc = {
num: 0,
increment() {
console.log(this === calc); // => true
this.num += 1;
return this.num;
}
};
// method invocation. this is calc
calc.increment(); // => 1
calc.increment(); // => 2A method from an object can be extracted into a separated variable. When calling the method using this variable, you might think that this is the object on which the method was defined. Correctly if the method is called without an object, then a function invocation happens: where this is the global object window or undefined in strict mode. Creating a bound function (using .bind()) fixes the context, making it the object that owns the method.
Arrow function Invocation
Arrow-функции не имеют собственного this и оно для их определяется по правилам лексической области видимости. Простыми словами, Arrow функции заимствуют this из окружающей их области видимости.
Constructor Invocation
Constructor invocation is performed when new keyword is followed by an expression that evaluates to a function object. When a property accessor myObject.myFunction is preceded by new keyword, JavaScript will execute a constructor invocation, but not a method invocation.
In a constructor invocation this is the newly created object:
function Country(name, traveled) {
this.name = name ? name : "United Kingdom";
this.traveled = Boolean(traveled); // transform to a boolean
}
Country.prototype.travel = function() {
this.traveled = true;
};
// Constructor invocation
const france = new Country("France", false);
// Constructor invocation
const unitedKingdom = new Country();
france.travel(); // Travel to FranceUsing a function invocation to create objects is a potential problem (excluding factory pattern), because some constructors may omit the logic to initialize the object when new keyword is missing.
function Vehicle(type, wheelsCount) {
this.type = type;
this.wheelsCount = wheelsCount;
return this;
}
// Function invocation
const car = Vehicle("Car", 4);
car.type; // => 'Car'
car.wheelsCount; // => 4
car === window; // => trueIndirect Invocation
Indirect invocation is performed when a function is called using .call() or .apply() methods. this is the first argument of .call() or .apply() in an indirect invocation
The method
.call(thisArg, arg1, arg2, ...)accepts the first argumentthisArgas the context of the invocation and a list of argumentsarg1, arg2, ...that are passed as arguments to the called function.The method
.apply(thisArg, [arg1, arg2, ...])accepts the first argumentthisArgas the context of the invocation and an array-like object of values[arg1, arg2, ...]that are passed as arguments to the called function.
function increment(number) {
return ++number;
}
increment.call(undefined, 10); // => 11
increment.apply(undefined, [10]); // => 11The spread operator (
...) mostly replacesapply().
this propagation
this propagationСуществует проблема, когда функция-callback, вызванная внутри другой функции имеет свой собственный this, который равен или глобальному объекту, или undefined (в строгом режиме).
В следующем примере this.name внутри функции выдаст ошибку, поскольку внутри анонимной функции this = undefined.
const obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop() {
'use strict';
this.friends.forEach(function (friend) { // (1)
console.log(this.name + ' knows ' + friend); // (2)
});
}
};Существует четыре способа решения этой проблемы:
Введение дополнительной временной переменной:
loop() {
'use strict';
const self = this;
this.friends.forEach(function (friend) {
console.log(self.name + ' knows ' + friend);
});
}Использование
bindдля явного заданияthis:
loop() {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name + ' knows ' + friend);
}.bind(this));
}Передача
thisпараметра в функциюforEach:
loop() {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name + ' knows ' + friend);
}, this);
}Использование arrow-functions в качестве функции обратного вызова (рекомендуемый):
loop() {
'use strict';
this.friends.forEach((friend) => {
console.log(this.name + ' knows ' + friend);
});
}Activation Object
При вызове функции создается объект активации, остающийся для вас невидимым. Это скрытая структура данных, которая содержит информацию и связывает все то, что функция должна выполнить, а также содержит обратный адрес активации вызывающей функции.
В языках, подобных C, объекты активации размещаются в стеке. Они покидают стек (или выводятся из него), когда функция возвращает управление. В JavaScript происходит иначе. Объекты активации JavaScript размещает в куче, как обычные объекты. При возвращении функцией управления объекты активации не проходят автоматическую деактивацию. Вместо этого объект активации может выживать, пока на него есть ссылка. Объекты активации подпадают под сборку мусора, как и обычные объекты.
В объекте активации содержатся:
ссылка на функциональный объект;
ссылка на объект активации вызывающей функции. Она используется инструкцией
returnдля возврата управления;информация о возвращении, которая применяется для продолжения вы- полнения кода после вызова. Обычно это адрес инструкции, выполняемой сразу же после вызова функции;
параметры функции, инициализированные аргументами;
переменные функции, инициализированные значением
undefined;временные переменные, используемые функциями для вычисления сложных выражений;
содержимое
this, которое может быть ссылкой на интересующий объект, если функциональный объект был вызван как метод.
В функциональном объекте содержатся также два скрытых свойства:
ссылка на исполняемый код функции;
ссылка на объект активации, который был активен в момент создания функ- ционального объекта. Это делает возможным создание замыкания. Функция может использовать это скрытое свойство для доступа к переменным той функции, которая ее создала.
Last updated
Was this helpful?