Пропустить правильный контекст "this" для вызова обратного вызова setTimeout?
Как передать контекст в setTimeout
? Я хочу называть this.tip.destroy()
, если this.options.destroyOnHide
после 1000 мс. Как я могу это сделать?
if (this.options.destroyOnHide) {
setTimeout(function() { this.tip.destroy() }, 1000);
}
Когда я попробую выше, this
относится к окну.
Ответы
Ответ 1
EDIT: В заключение, еще в 2010 году, когда этот вопрос был задан самый распространенный способ решения этой проблемы в том, чтобы сохранить ссылку на контекст, где setTimeout
производится вызов функции, поскольку setTimeout
выполняет функцию с this
, указывая на глобальный объект:
var that = this;
if (this.options.destroyOnHide) {
setTimeout(function(){ that.tip.destroy() }, 1000);
}
В спецификации ES5, выпущенной за год до этого, в нем был представлен метод bind
, он не был предложен в первоначальном ответе, потому что он еще не получил широкой поддержки, и вам потребовались полифилы, чтобы использовать его, но теперь он везде:
if (this.options.destroyOnHide) {
setTimeout(function(){ this.tip.destroy() }.bind(this), 1000);
}
Функция bind
создает новую функцию с предварительно заполненным значением this
.
Теперь в современном JS именно эту проблему решают функции стрелок в ES6:
if (this.options.destroyOnHide) {
setTimeout(() => { this.tip.destroy() }, 1000);
}
Функции стрелок не имеют собственного значения this
, когда вы обращаетесь к нему, вы получаете доступ к значению this
включающей лексической области видимости.
HTML5 также стандартизировал таймеры еще в 2011 году, и теперь вы можете передавать аргументы в функцию обратного вызова:
if (this.options.destroyOnHide) {
setTimeout(function(that){ that.tip.destroy() }, 1000, this);
}
Смотрите также:
Ответ 2
Есть готовые ярлыки (синтаксический сахар) к функции-обертки @CMS, на которую отвечает. (Ниже предполагается, что контекст, который вы хотите, this.tip
.)
Если вы настроили браузер совместимый с ECMA-262, 5-е издание (ECMAScript 5) или Node.js, вы можете использовать Function.prototype.bind
. Вы можете опционально передать любые аргументы функции для создания частичных функций.
fun.bind(thisArg[, arg1[, arg2[, ...]]])
Опять же, в вашем случае, попробуйте следующее:
if (this.options.destroyOnHide) {
setTimeout(this.tip.destroy.bind(this.tip), 1000);
}
Та же функциональность также была реализована в Prototype (любые другие библиотеки?).
Function.prototype.bind
может быть реализовано следующим образом, если вам нужна пользовательская обратная совместимость (но, пожалуйста, обратите внимание на примечания).
Для передовых разработок (2015) вы можете использовать функции толстых стрелок, которые часть спецификации ECMAScript 2015 (Harmony/ES6/ES2015) (примеры).
выражение функции стрелки (также известное как функция стрелки жира) имеет более короткий синтаксис по сравнению с функциональными выражениями и лексически связывает значение this
[...].
(param1, param2, ...rest) => { statements }
В вашем случае попробуйте следующее:
if (this.options.destroyOnHide) {
setTimeout(() => { this.tip.destroy(); }, 1000);
}
Если вы уже используете jQuery 1.4+, есть готовая функция для явной настройки контекста this
для функции.
jQuery.proxy(): принимает функцию и возвращает новую, которая всегда будет иметь определенный контекст.
$.proxy(function, context[, additionalArguments])
В вашем случае попробуйте следующее:
if (this.options.destroyOnHide) {
setTimeout($.proxy(this.tip.destroy, this.tip), 1000);
}
Он доступен в Underscore.js, а также lodash, как _.bind(...)
1, 2
bind Связывает функцию с объектом, что означает, что всякий раз, когда вызывается функция, значение this
будет объектом. Необязательно, свяжите аргументы с функцией, чтобы предварительно заполнить их, также известную как частичное приложение.
_.bind(function, object, [*arguments])
В вашем случае попробуйте следующее:
if (this.options.destroyOnHide) {
setTimeout(_.bind(this.tip.destroy, this.tip), 1000);
}
bind jquery underscore.js ecmascript-5 prototypejs node.js
Ответ 3
В браузерах, отличных от Internet Explorer, вы можете передавать параметры функции вместе после задержки:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
Итак, вы можете сделать это:
var timeoutID = window.setTimeout(function (self) {
console.log(self);
}, 500, this);
Это лучше с точки зрения производительности, чем просмотр области (кэширование this
в переменную за пределами выражения тайм-аута/интервала), а затем создание закрытия (с помощью $.proxy
или Function.prototype.bind
).
Код, чтобы заставить его работать в IE с Webreflection:
/*@cc_on
(function (modifierFn) {
// you have to invoke it as `window` property so, `window.setTimeout`
window.setTimeout = modifierFn(window.setTimeout);
window.setInterval = modifierFn(window.setInterval);
})(function (originalTimerFn) {
return function (callback, timeout){
var args = [].slice.call(arguments, 2);
return originalTimerFn(function () {
callback.apply(this, args)
}, timeout);
}
});
@*/
Ответ 4
ПРИМЕЧАНИЕ. Это не будет работать в IE
var ob = {
p: "ob.p"
}
var p = "window.p";
setTimeout(function(){
console.log(this.p); // will print "window.p"
},1000);
setTimeout(function(){
console.log(this.p); // will print "ob.p"
}.bind(ob),1000);
Ответ 5
Если вы используете underscore
, вы можете использовать bind
.
например.
if (this.options.destroyOnHide) {
setTimeout(_.bind(this.tip.destroy, this), 1000);
}