OO Javascript — правильная обработка области видимости?

#javascript #jquery #scope

#javascript #jquery #область видимости

Вопрос:

Я написал короткий и неполный пример (ради этого вопроса), в котором делается попытка использовать jquery для суммирования ширины группы изображений. У меня возникли некоторые проблемы с выяснением того, как обрабатывать область видимости в сложных приложениях OO javascript.

 function imageContainer(){
      this.selector = "img.inContainer";

      this.width = function(){
        var tmp = 0;
        $(this.selector).each(function(){
          // use load to preload images
          $(this).load(function(){ 
             // our 'this' pointer to the original object is long gone,
             // so is it even possible to accumulate a sum without using
             // globals? Ideally, I'd like to increment a temporary value
             // that exists within the scope of this.width();
             tmp =$(this).width();
          });
        });
        // I'm thinking that returning here is problematic, because our
        // call to each() may not be complete?
        return tmp;
      }

      this.construct = function(){
        alert(this.width());
      }

      this.construct();
}
  

На самом деле я не ищу способ обойти эту проблему, я хотел бы знать, как это следует делать — таким образом, чтобы не нарушать инкапсуляцию. Я упускаю что-то очевидное?

Большое спасибо.

Ответ №1:

 function imageContainer() {
    this.selector = "img.inContainer";

    this.width = function(cb) {
        var tmp = 0;
        var len = this.length;
        var count = 0;
        $(this.selector).each(function() {
            // use load to preload images
            var that = this;
            // load is ajax so it is async
            $(this).load(function() {
                tmp  = $(that).width();
                if (  count === len) {
                    // counted all.
                    cb(tmp);
                }
            });
        });
    };

    this.construct = function(cb) {
        this.width(function(data) {
            alert(data);
        });
    };

    this.construct();
}
  

Добро пожаловать в ajax. Вы выполняете кучу операций параллельно асинхронно. Итак, вам нужно отслеживать, сколько из них завершено, и запускать обратный вызов, когда все будут завершены.

Любая асинхронная операция, подобная .load требует от вас либо блокировки на 100 мс, либо изменения вашего API для использования обратных вызовов.

Вместо этого вы также можете использовать var that = this $.proxy шаблон.

 // load is ajax so it is async
$(this).load($.proxy(function() {
    tmp  = $(this).width();
    if (  count === len) {
        // counted all.
        cb(tmp);
    }
}, this));
  

Поскольку у вас есть конструкция выполнения n задач ajax перед запуском обратного вызова, вы можете обобщить это с помощью некоторого количества сахара.

 this.width = function(cb) {
    // map all images to deferred objects, when deferred objects are resolved
    $.when($.makeArray($(this.selector).map(function() {
        var def = $.Deferred();
        $(this).load(function() {
            def.resolve();
        });
        return def;
    // then sum the widths and fire the callback.
    }))).then($.proxy(function(data) {
        var tmp = 0;
        $(this.selector).each(function() {
             tmp =$(this).width();
        });
        cb(tmp);
    }, this));
};
  

Обратите внимание, что здесь я действительно хочу использовать $.fn.reduce , но его не существует. Это могло бы быть

 // reduce the set of images to the sum of their widths.
cb($(this.selector).reduce(0, function(memo, key, val) {
    return memo   $(this).width();
}));
  

Если подумать, этот сахар ничуть не упрощает работу, по крайней мере, сейчас она больше похожа на LISP, чем на C.