В почти всех примерах вида knockout.js моделируется строка var self = this
, а затем все локальные переменные являются ссылками как self.variableName
. В чем преимущество этого при использовании this.variableName
?
Какое преимущество использования `var self = this` в моделях просмотра knockout.js
Ответ 1
Обычно основной причиной использования этого подхода является то, что текущий this
доступен для подфункций или замыканий. Например:
var myObject = {
param: 123,
method: function(){
alert( this.param );
},
method2: function(){
setTimeout(function(){
alert( this.param );
},100);
}
}
В приведенном выше вызове myObject.method
вы получите правильное предупреждение 123
. Однако вызов myObject.method2
даст вам undefined
. Это связано с тем, что this
внутри анонимной функции, используемой с setTimeout
, не относится к myObject
, в зависимости от интерпретатора JavaScript это указывает на разные вещи. Однако, если у вас есть:
method2: function(){
var self = this;
setTimeout(function(){
alert( self.param );
},100);
}
Это работает, потому что текущее состояние this
— в правой точке; и всегда будет ссылаться на myObject
для каждой области видимости, доступной для нее.
Проблема не ограничивается использованием setTimeout
. В любой момент, когда у вас есть анонимные функции, подфункции или замыкания, этот трюк пригодится. Иногда люди используют self
или that
или что-то более подробное описание в зависимости от того, что представляет текущая ссылка.
вместо сохранения в качестве переменной
Существует альтернатива использованию self
или любой другой переменной, чтобы "запомнить" состояние этого в любой конкретной точке, а именно "привязать" ваши анонимные или вспомогательные функции к определенному контексту. Многие современные интерпретаторы теперь поддерживают метод Function.prototype.bind
, который можно использовать таким образом:
var method = function(){
console.log(this);
};
var methodWithWindow = method.bind(window);
var methodWithDocument = method.bind(document);
var methodWithObject = method.bind({random:"object"});
Вызов каждого из связанных методов в свою очередь даст вам следующий вывод консоли:
Window
Document
Object {random:"object"}
Если вы хотите поддерживать старые браузеры, вы можете использовать polyfill, или если вы предпочитаете гораздо более простую реализацию, которая не беспокоится о привязке аргументов также. Основы выполнения кода привязки следующие:
!Function.prototype.bind && (Function.prototype.bind = function(context){
var method = this;
return function(){
method.apply(context, arguments);
}
})
Итак, как бы выглядел исходный пример с помощью bind?
method2: function(){
setTimeout((function(){
console.log(this); // `this` will be the same as the `this` passed to bind.
}).bind(this),100);
}
Как вы можете видеть выше, после привязки возвращаемая функция (закрытие) сохраняет указанный контекст; поэтому он может быть передан туда, где вы хотите, и по-прежнему сохраняйте ссылку this
на нужный объект. Это полезно в примере method2
, потому что мы собираем метод вверх с нашим текущим контекстом и передаем его в setTimeout
, который позже выполнит связанный метод (после того, как мы выйдем из текущего выполнения блока).
То же самое происходит при использовании self
или любой другой переменной. Переменная будет записана в цепочке видимости функции и все равно будет доступна для доступа, когда функция в конечном итоге будет вызвана. Преимущество использования bind
заключается в том, что вы можете легко переопределить контекст, если вы этого пожелаете, вам придется закодировать свои собственные специальные методы, чтобы сделать это, чтобы переопределить переменную self
.
ПРЕДУПРЕЖДЕНИЕ: Стоит отметить, что при связывании функции возвращается новая функция. Это может привести к запутывающим ситуациям, если вы смешаете связанные функции с прослушивателями событий, а затем попытаетесь удалить слушателей, используя исходную функцию, а не связанную версию.
Кроме того, поскольку привязка возвращает новую функцию, если вы привязываете связанную функцию, вы фактически обертываете функцию в функцию, с другой функцией. Вы должны знать об этом, потому что это влияет на производительность и окажется более сложным для управления с точки зрения предотвращения утечек памяти. Мой предпочтительный подход к привязке заключается в использовании закрытий с их собственными методами деконструкции (т.е. Полагаться на себя, но убедитесь, что у вас есть методы, чтобы свести его к нулю), но это требует более продуманного мышления и не столь актуально в небольших проектах JS; или одно выкл. функции привязки — особенно если связанный метод никогда не попадает в какую-либо ссылку.
без self и bind?
Также стоит упомянуть, что иногда вы можете добиться того же результата без использования bind
вообще, а вместо этого использовать apply
— который должен быть изначально доступен во всем, что вы можете использовать. Основное различие заключается в том, что ничто не обернуто функцией, а вызов apply фактически выполняет функцию там, а затем с другим контекстом — первый аргумент передан для применения.
var externalMethod = function(){
console.log(this); // will output myObject when called below
};
var myObject = {
method2: function(){
externalMethod.apply(this);
}
};
Что такое this
?
Чтобы подробнее рассказать об этом ответе на this
— до того, как последние комментарии будут удалены. this
будет ссылаться на одну из четырех вещей, в зависимости от того, как была вызвана функция, в которой вы используете ее внутри:
myObject.method()
Вышеуказанное будет иметь this
of myObject
, если только method
не выполнило операцию .bind(context)
. В этом случае this
будет любым последним связанным контекстом.
unattachedFunction()
Будет иметь this
глобального контекста (обычно window
в средах браузера), если только unattachedFunction
не выполнила операцию .bind(context)
. В этом случае this
будет любым последним связанным контекстом.
anyFunction.apply(otherObject)
или
anyFunction.call(otherObject)
Оба всегда будут иметь this
из otherObject
, потому что вызов таким образом переопределит любую привязку.
new myObject()
Будет иметь this
, который ссылается на новый экземпляр myObject
, это переопределит любую привязку.
Простой мысленный эксперимент
Учитывая все сказанное выше, что бы this
было внутри referencedMethod
?
var referencedMethod = myObject.method;
referencedMethod();
Правильно! это будет глобальный контекст. Вот почему, если вы хотите делиться методами с другими объектами или кодом и— но все же сохранить первоначального владельца как context — вам действительно нужно либо привязать, либо сохранить функцию в комплекте с ее объектом-владельцем, чтобы вы могли позвонить или применить.
Ответ 2
Self
используется, чтобы убедиться, что исходный this
поддерживается внутри объекта.
Это полезно при использовании обработчиков событий и т.д.
Вы можете прочитать об этом здесь
Первый ответ охватывает его в основном, также он показывает хорошую ссылку. Проверьте это.
Ответ 3
Используется для справочных целей. this
под Javascript ведет себя иначе, чем на других языках. Для более подробной информации смотрите MDN Docs на этом