Несколько экземпляров объекта JavaScript, не удается корректно получить доступ к свойствам внутри обработчика событий

#javascript #closures #dom-events

#javascript #замыкания #dom-события

Вопрос:

У меня есть рабочая версия загрузчика файлов перетаскиванием HTML5. Я редактировал код JS для поддержки нескольких загрузок файлов на одной странице. Я столкнулся с проблемой, пытаясь получить доступ к свойствам «экземпляра» в методах, которые зарегистрированы как события.

Проблема показана приведенным ниже кодом в методе this.drop . Причиной существования this.$$upload_self свойства является доступ к данным через это свойство. Например, я не могу использовать this ключевое слово внутри this.drop функции, потому что при возникновении события, this не ссылающегося на мой «экземпляр».

Я не уверен, что создание $$upload_self было хорошей идеей.

Область новых экземпляров создана следующим образом:

   var recording_upload = new upload();
  recording_upload.init(recording_upload, ...);
  

Код загрузки файла перетаскиванием:

     var upload = function() {
    this.$$upload_self = null;
    this.$drop = null;
    this.$status = null;
    this.$progress = null;
    this.maxNumberOfFiles = null;
    ...
    
    this.init = function (self, pUrl, maxNmbOfFiles, dropArea, progress, status) {
        $$upload_self = self;
        $$upload_self.postUrl = pUrl;
        $$upload_self.maxNumberOfFiles = maxNmbOfFiles;

        $$upload_self.$drop = $("#"   dropArea);
        $$upload_self.$progress = $("#"   progress);
        $$upload_self.$status = $("#"   status);

        $$upload_self.$drop.bind('dragenter', $$upload_self.enter);
        $$upload_self.$drop.bind('dragleave', $$upload_self.leave);
        $$upload_self.$drop.bind('drop', $$upload_self.drop);
    };
    
    this.enter = function (e) {
        $(e.target).addClass('hover');
        return false;
    };

    this.leave = function (e) {
        $(e.target).removeClass('hover');
        return false;
    };
    
    this.drop = function (e, _this) {
        $(e.target).removeClass('hover');
        var files = e.originalEvent.dataTransfer.files;

        if (files.length > $$upload_self.maxNumberOfFiles) { // for example, here $$upload_self references always last instance...
            $$upload_self.displayErrorMessage('Error: You can only drop '   $$upload_self.maxNumberOfFiles   ' file(s) at time.');
            return false;
        }
     ...
    };
    ...
}
  

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

Любая помощь очень ценится.

Ответ №1:

Вы могли бы вообще отказаться от new ключевого слова и использовать новое замыкание для каждого экземпляра upload .

РЕДАКТИРОВАТЬ: обновлено, чтобы избежать потенциального сглаживания глобального this .

 var upload = function(pUrl, maxNmbOfFiles, dropArea, progress, status) {
    return {
        postUrl: pUrl,
        ...
        drop: function(e) {
            ...
            if (files.length > this.maxNumberOfFiles) {
                this.displayErrorMessage(...);
            }
            ...
        },
        ...
    };
};

...

var someUpload = upload(...);
  

Ответ №2:

Попробуйте выполнить поиск «области видимости». В качестве примера посмотрите, как это реализовано в ExtJS.

Комментарии:

1. На самом деле это не проблема области видимости; это проблема с семантикой вызовов функций и this .

2. Это «область видимости» в терминах ExtJS. Не переменная область видимости, но может быть некоторый контекст — я не знаю более правильного универсального слова.

Ответ №3:

В современном браузере вы можете сделать это:

   $$upload_self.$drop.bind('dragleave', $$upload_self.leave.bind($$upload_self));
  

Для более старых версий IE вы можете сделать это:

   $$upload_self.$drop.bind('dragleave', function() { $$upload_self.leave(); });
  

На самом деле метод «.bind()» для всех функциональных объектов в новых браузерах просто создает для вас небольшую промежуточную функцию, по сути, похожую на второй пример. На странице документов Mozilla для «.bind()» есть очень хороший блок кода, который вы можете использовать в качестве исправления «полизаполнения» для браузеров, которые не поддерживают «.bind()» изначально.