Передача области для функции обратного вызова/привязки
Я пытаюсь передать область действия функции методу обратного вызова. Проблема, с которой я сталкиваюсь, заключается в том, что я получаю объектную область, которая не предоставляет мне доступ к параметрам и локальным переменным в исходной функции. Мое понимание "this" означает текущий контекст (будь то окно или какой-либо объект) в дополнение к локально объявленным переменным и параметрам. [цитирует Ричарда Корнфорда отличную работу в http://jibbering.com/faq/notes/closures/ в разделе "Контексты выполнения" ]. Я также понимаю, что переменные в JavaScript имеют область действия (что, если они объявлены внутри функции, они доступны только из этой функции).
С этим пониманием, в новой среде, я пытаюсь закодировать шаблон, который я сделал много для моего предыдущего работодателя, вызвав асинхронный метод, указав обработчик обратного вызова и передав мою текущую область, ожидая, что он будет доступен в метод обратного вызова. Я не считаю, что это имеет место в моей нынешней среде. (раскрытие: я использовал ExtJS в своей предыдущей среде... заставляя меня чувствовать себя так же, как будто я был слишком умен с каркасом, делая предположения о том, что происходит).
Мой простой тестовый код демонстрирует, что я пытаюсь сделать и что не работает.
function myHandler(data, ctx) {
console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined
console.log(JSON.stringify(data));
}
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback.call(scope||this,result, scope);
}
}
}
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler, this); // scope object is this
}
lookup();
Проблема в том, что 'this', переданный MySvcWrap.doWork, является глобальным объектом Window в этом случае. Мое намерение - передать контекст выполнения функции в myHandler.
То, что я пробовал. Если вместо 'this' я передаю объект, который работает, например:
function myHandler(data, ctx) {
console.log('myHandler(): this.bar: ' + this.bar); // <- no prob: this.bar
console.log(JSON.stringify(data));
}
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler, {bar: bar}); // scope object is just object
}
Мне нужно, чтобы кто-то завязал меня за голову здесь... когда я проходил 'this' в моем первом случае, конечно, это глобальная область (я нахожусь в глобально определенной функции)... Моя проблема в том, что я подумал, передавая область, к которой у меня был доступ к локально определенным переменным и параметрам w/в этом контексте... Я качаю свое предыдущее понимание JS?? Как выполнить эту задачу?
Ответы
Ответ 1
btw, несколько слов о областях в вашем коде:
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler, this); // this here points to window object and not to the function scope
}
так что это то же самое, что и запись:
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler, window);
}
это так, потому что вы определяете функцию поиска глобально. внутри doWork
, когда вы пишете this
, он указывает на объект MySvcWrap
(потому что эта функция определена внутри этого объекта).
Если ваша функция обратного вызова должна видеть переменную bar
, она должна быть определена в той же области, что и
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback.call(scope||this,result, scope);
}
}
}
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang',
function (data, ctx) {
console.log('myHandler(): bar: ' + bar);
console.log(JSON.stringify(data));
},
this); // scope object is this
}
lookup();
в этом случае вы отправляете анонимную функцию в качестве обратного вызова, она определяется внутри функции поиска, чтобы она имела доступ к ее локальным переменным; моя консоль показывает мне в этом cae:
myHandler(): bar: food
{"colors":["red","green"],"name":"Jones","what":"thang"}
чтобы упростить поддержку, вы можете определить функцию myHandler внутри функции поиска:
function lookup() {
var bar = 'food'; // local var
var myHandler = function(data, ctx) {
console.log('myHandler(): bar: ' + bar);
console.log(JSON.stringify(data));
};
MySvcWrap.doWork('thang', myHandler, this); // scope object is this
}
С другой стороны, почему одна функция должна иметь доступ к локальным переменным другой функции? Возможно, его можно переделать...
Еще один способ заставить ваш код работать - использовать анонимную функцию вместо поиска (будет работать, если вы просто объявите и выполните эту функцию один раз):
(function() {
var bar = 'food';
function myHandler(data, ctx) {
console.log('myHandler(): bar: ' + bar);
console.log(JSON.stringify(data));
}
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback.call(scope||this,result, scope);
}
}
}
MySvcWrap.doWork('thang', myHandler, this);
}
)();
результат тот же, но никакой функции поиска больше нет...
И еще одна идея, чтобы заставить ее работать... На самом деле вам нужно определить обработчик обратного вызова в той же области, где определена переменная bar, поэтому ее можно сделать немного сложной, но как альтернатива:
function myHandler(bar) { // actually in this case better to call it createHandler
return function(data, ctx) {
console.log('myHandler(): bar: ' + bar);
console.log(JSON.stringify(data));
}
}
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback.call(scope||this,result, scope);
}
}
}
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler(bar), this); // scope object is this
}
И немного ресурсов для чтения о области обзора и закрытия JavaScript:
Ответ 2
Код будет работать, если он был структурирован таким образом.
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback.call(scope||this,result, scope);
}
}
}
function lookup() {
var bar = 'food'; // local var
function myHandler(data, ctx) {
console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined
console.log(JSON.stringify(data));
}
MySvcWrap.doWork('thang', myHandler, this); // scope object is this
}
lookup();
Функция myHandler может обращаться к локальным переменным поиска, поскольку она закрыта им, поэтому закрывается.
Вероятно, я попытался бы достичь такого же результата, структурируя код следующим образом.
function myHandler(data, bar, ctx) {
console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined
console.log(JSON.stringify(data));
}
MySvcWrap = {
doWork: function(p1, callback) {
var result = {colors: ['red', 'green'], name:'Jones', what: p1};
if (callback) {
callback(result);
}
}
}
function lookup() {
var bar = 'food'; // local var
MySvcWrap.doWork('thang', myHandler.bind(this, bar)); // callback function is bound to the scope
}
lookup();
Вместо того, чтобы передавать область действия, я использую bind внутри метода поиска. Используя bind, я также могу добавить локальные переменные, которые я хочу передать из этой области.
Конечно, поскольку привязка недоступна в старых браузерах, вам нужно либо добавить ее через фреймворк, либо использовать один из многих небольших фрагментов кода, которые добавляют этот метод, если он не существует.
Ответ 3
Я был немного уверен в своем первом ответе и оставил некоторые пробелы, как указано Maxym в комментариях ниже, спасибо за то, что сообщил мне:) Это то, что я получаю за попытку опубликовать быстро, прежде чем возвращаться к работе: P
То, что я пытался получить в своем первом ответе, - это если вы хотите использовать привязку для доступа к переменным в функции lookup
из функции myHandler
(которая выходит за пределы lookup
), вам не придется используйте var
, но this
.
Использование var
сделает его доступным только для lookup's
частной области видимости и любых функций, вложенных в нее, как показали другие ответы (и, вероятно, лучший маршрут)
Ниже приведен обновленный пример того, как передать область поиска myHandler, оставляя myHandler полностью вне области поиска. Надеюсь, это поможет немного пролить свет на:
"Проблема заключается в том, что я получаю объектную область, которая не предоставляет мне доступ к параметрам и локальным переменным в исходной функции".
Как указано в комментариях к моему предыдущему ответу, this
может стать сложным, и вы можете добавить вещи в область global
, если не будете осторожны, как это было в моем первом примере.:( Итак, я добавил немного хакерской проверки, чтобы увидеть, какая область this
делает это не для window
для демонстрационных целей...
Живой пример
function myHandler(data, ctx) {
console.log('myHandler(): bar: ' + this.bar); // <- must use `this`
console.log('myHandler(): privateBar: ' + this.privateBar); // <- undefined because it privately scoped
console.log(JSON.stringify(data));
}
MySvcWrap = {
doWork: function(p1, callback, scope) {
var result = {
colors: ['red', 'green'],
name: 'Jones',
what: p1
};
if (callback) {
callback.call(scope || this, result, scope);
}
}
}
function lookup() {
if(this == window) return lookup.call(lookup); // <- my hack'ish check
var privateBar = 'private food'; // private local var
this.bar = 'food'; // public local var
MySvcWrap.doWork('thang', myHandler, this); // scope object is this
}
lookup();
console.log('global space - bar: ' + this.bar);