Я работал над проектом некоторое время, пытаясь выяснить то, что я делал неправильно, когда я наконец сузил "ошибку" к тому, которое ниже кода не работает, как я ожидал:
function Alpha()
{
this.onion = 'onion';
function Beta()
{
alert(this.onion);
}
Beta();
}
alpha1 = new Alpha();
// Alerts 'undefined'
Однако, если я изменяю код на это:
function Alpha()
{
var self = this;
this.onion = 'onion';
function Beta()
{
alert(self.onion);
}
Beta();
}
alpha1 = new Alpha();
// Alerts 'onion'
это работает как, я ожидал бы. После траты значительной части моей жизни кто-либо может объяснить, почему она работает как это?
Работает так, потому что каждая функция связана со своим собственным контекстом выполнения .
Однако есть и другие способы сделать это, например:
Использование call
или apply
для вызова функции:
function Alpha() {
this.onion = 'onion';
function Beta() {
alert(this.onion);
}
Beta.call(this);
}
var alpha1 = new Alpha();
// Alerts 'onion'
Новый стандарт ECMAScript 5th Edition представляет способ сохранить функцию context , метод Function.prototype.bind
:
function Alpha() {
this.onion = 'onion';
var Beta = function () {
alert(this.onion);
}.bind(this);
Beta();
}
var alpha1 = new Alpha();
// Alerts 'onion'
Мы можем сказать, что функция Beta
привязана , и независимо от того, как вы его вызываете, его значение this
будет неизменным.
Этот метод еще не получил широкой поддержки, в настоящее время только IE9pre3 включает его, но вы можете включить реализацию , чтобы он работал сейчас.
Теперь позвольте мне подробнее рассказать о том, как this
работает:
Значение this
существует в каждом контексте выполнения , а для кода функции устанавливается неявно, когда выполняется вызов функции, значение определяется в зависимости от того, как формируется ссылка .
В вашем примере, когда вы вызываете Beta ();
, поскольку он не привязан ни к какому объекту, мы можем сказать, что ссылка не имеет базового объекта , тогда это
значение будет относиться к глобальному объекту.
Другой случай возникает, когда вы вызываете функцию, которая связана как свойство объекта, например:
var obj = {
foo: function () { return this === obj;}
};
obj.foo(); // true
Как видите, вызываемая ссылка obj.bar ();
содержит базовый объект, которым является obj
, и значение this
внутри вызываемой функции будет ссылаться на него.
Примечание : ссылочный тип является абстрактным понятием, определенным для целей языковой реализации, подробности можно увидеть в спецификации.
Третий случай, когда значение this
устанавливается неявно, - это когда вы используете оператор new
, он будет ссылаться на вновь созданный объект, который наследуется от прототипа своего конструктора:
function Foo () {
return this; // `this` is implicitly returned when a function is called
} // with `new`, this line is included only to make it obvious
var foo = new Foo();
foo instanceof Foo; // true
Foo.prototype.isPrototypeOf(foo); // true
Когда вы вызываете функцию, не являющуюся членом (не вызываемую как someObject.method ()
), она запускается в контексте окна. Неважно, частная это функция или глобальная.
Вы можете сделать:
Beta.call(this);
call позволяет вам вызывать функцию, передавая контекст в качестве первого аргумента ( apply аналогичен, но список аргументов является массивом).
Однако мне непонятно (даже для простейшего примера), почему луковица является общедоступной, а бета-версия - частной.
Из JavaScript: Полное руководство, 5-е издание (книга rhino):
Когда функция вызывается как функция, а не метод,
это
ключевое слово относится к глобальному объект. Как ни странно, это правда даже когда вызывается вложенная функция (как функция) внутри содержащего метода который был вызван как метод:this
ключевое слово имеет одно значение в содержащий функцию, но (парадоксально) относится к глобальный объект в теле вложенная функция.Обратите внимание, что
this
является ключевым словом, а не имя переменной или свойства. JavaScript синтаксис не позволяет назначить значениеэто
.
Здесь следует обратить внимание на две вещи:
this
не является переменной, поэтому обычные правила захвата закрытия не применяются. this
является «отскоком» при каждом вызове функции, будь то метод, обычный вызов функции или через new
. Поскольку вы выполняете обычный вызов функции (при вызове бета-версии), this
привязывается к «глобальному объекту».