Далее следует выдержка из Закрытие: Полное руководство, автор Michael Bolin . Это может выглядеть немного длинным, но оно насыщено глубоким пониманием. Из «Приложения Б. Часто неверно понимаемые концепции JavaScript ":
Что означает this
, когда вызывается функция
При вызове функции вида foo.bar.baz()
объект foo.bar
называется получателем. Когда функция вызывается, это получатель, который используется в качестве значения для this
:
var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
for (var i = 0; i < arguments.length; i++) {
this.value += arguments[i];
}
return this.value;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);
Если нет явного получателя при вызове функции, тогда глобальный объект становится получателем. Как объяснено в "Goog. global "на странице 47, window - это глобальный объект, когда JavaScript выполняется в веб-браузере. Это приводит к неожиданному поведению:
var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN
Несмотря на то, что obj.addValues
и f
ссылаются на одну и ту же функцию, при вызове они ведут себя по-разному, потому что значение получателя различно в каждом вызове. По этой причине при вызове функции, которая ссылается на this
, важно убедиться, что this
будет иметь правильное значение при вызове. Для ясности, если бы в теле функции не было ссылки на this
, то поведение f(20)
и obj.addValues(20)
было бы таким же.
Поскольку функции являются первоклассными объектами в JavaScript, они могут иметь свои собственные методы. Все функции имеют методы call()
и apply()
, которые позволяют переопределить приемник (т.е. е. объект, на который ссылается this
) при вызове функции. Подписи метода следующие:
/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;
Обратите внимание, что единственное различие между call()
и apply()
состоит в том, что call()
получает параметры функции в качестве отдельных аргументов, тогда как apply()
получает их как один массив:
// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);
Следующие вызовы эквивалентны, так как f
и obj.addValues
относятся к одной и той же функции:
obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);
Однако, поскольку ни call()
, ни apply()
не используют значение своего собственного получателя для замены аргумента получателя, когда он не указан, следующее не будет работать:
// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);
Значение this
никогда не может быть null
или undefined
при вызове функции. Когда null
или undefined
предоставляется в качестве получателя для call()
или apply()
, вместо этого используется глобальный объект в качестве значения для получателя. Поэтому предыдущий код имеет тот же нежелательный побочный эффект, что и добавление свойства с именем value
к глобальному объекту.
Может быть полезно думать о функции как о не имеющей знания о переменной, которой она назначена. Это помогает укрепить идею о том, что значение этого будет связано, когда функция вызывается, а не когда она определена.
Конец выписки.