Установка значения литерального свойства объекта с помощью асинхронного обратного вызова. - PullRequest
2 голосов
/ 23 декабря 2010

Я создаю автономный объект утилиты javascript, который обнаруживает расширенные функции браузера.В идеале мой объект должен выглядеть примерно так:

Support = {
    borderRadius : false, // values returned by functions
    gradient     : false, // i am defining 
    dataURI      : true
};

Моя текущая проблема связана с кодом, который я адаптирую с сайта Weston Ruter , который обнаруживает поддержку dataURI.Он пытается использовать JavaScript для создания изображения с источником dataURI и использует обратные вызовы onload / onerror для проверки ширины / высоты.Поскольку onload асинхронный, я теряю область действия, и возвращение true / false не назначает true / false моему объекту.

Вот моя попытка:

Support = {
    ...
    dataURI : function(prop) {
        prop = prop; // keeps in closure for callback
        var data = new Image();
        data.onload = data.onerror = function(){
            if(this.width != 1 || this.height != 1) {
                prop = false;
            }
            prop = true;
        }
        data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
        return -1;
    }(this)
};

Я выполняю анонимныйфункция немедленно, передавая это (что я надеялся, было ссылкой на Support.dataURI), но, к сожалению, ссылается на объект окна - так что значение всегда -1.Я могу заставить его работать, используя внешне определенную функцию для присвоения значения после того, как объект поддержки создан ... но я не думаю, что это очень чисто в этом смысле.Есть ли способ для этого быть автономным?Может ли функция литерала объекта ссылаться на свойство, которому оно назначено?

EDIT -----------------------------------
Не совсем ответ на мой вопрос (как сформулировано), поэтому я не собираюсь публиковать дополнительный ответ, НО ... Я решил вместо этого использовать одноэлементный объектобъекта буквального.Вот рабочий код:

Support = new function Support() {
    var that = this;
    this.dataURI = function() {
        var data = new Image();
        data.onload = data.onerror = function(){
            if(this.width != 1 || this.height != 1) {
                that.dataURI = false;
            } else {
                that.dataURI = true;
            }            
        }
        data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
        return that.dataURI;
    }();
};

Ответы [ 3 ]

3 голосов
/ 24 декабря 2010

Может ли функция литерала объекта ссылаться на свойство, которому оно назначено?

Да, может.Каждая функция имеет локальную переменную arguments , которая имеет свойство callee, которое ссылается на вызываемую функцию.Поэтому, если вы хотите сослаться на Support.dataURI, вы можете просто использовать это:

var Support = {
    ...
    dataURI: function(prop)
    {
        // Do whatever here with arguments.callee
    }        
};

Однако я не думаю, что это действительно то, что вы хотите.Похоже, вы пытаетесь сослаться на свойство name , а не на значение - в этом случае ваш ответ - нет.У метода JavaScript нет возможности узнать, в каких свойствах он хранится;действительно, он может храниться в нескольких свойствах одновременно.Например:

var Support = {
    ...
    dataURI: function(prop)
    {
    }        
};
Support.somethingElse = Support.dataURI;

Здесь Support имеет два свойства, которые оба указывают на один и тот же метод.Однако сам метод не может знать, какая ссылка была вызвана.А поскольку объект Support еще не объявлен, вы не можете ссылаться на него.

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

Боюсь, вы можете объявить объект как можно лучше, а затем установить его свойство dataURI.


В ответ на ваше изменение

Прежде всего, вам не нужно ключевое слово "new" перед "функцией".Тем не менее, я не вижу, как это может работать в любом браузере.Если вы упростите его, строки 3–14 вашего кода будут иметь вид «this.dataURI = fn ();».Вот как работает такой оператор:

  1. Значение определяется путем выполнения fn()
  2. Значение присваивается свойству dataURI.

В момент выполнения fn(), dataURI еще не был назначен, поэтому нет способа получить доступ к его (правильному) значению, чтобы вернуть его.

Кроме того, у вас есть(по крайней мере, потенциально) асинхронный процесс (ожидание загрузки изображения или ошибки), поэтому значение не будет даже установлено обратным вызовом.По сути, вы пытаетесь обернуть асинхронный процесс (onload событие) в синхронный (вызов функции).

Наконец, вам не нужно создавать и выполнять функцию здесь.Просто избавьтесь от этого:

var Support = function() {
    var that = this;

    var data = new Image();
    data.onload = data.onerror = function(){
        if(this.width != 1 || this.height != 1) {
            that.dataURI = false;
        } else {
            that.dataURI = true;
        }            
    }
    data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";

};

Но помните: все еще нет гарантии (по крайней мере, я знаю), что обработчики onload и onerror будут запускаться синхронно (немедленно), когда вы устанавливаетеисточник - на самом деле, может даже быть гарантия, что они не будут!Таким образом, вы, вероятно, добавите обратный вызов к вашему Support объекту и выполните его, как только будет установлен dataURI.Тогда, однако, вам придется остерегаться того, что onload или onerror выполняется синхронно.Примерно так должно работать:

var Support = function() {
    var that = this;

    this.getDataURI = function()
    {
        var data = new Image();
        data.onload = data.onerror = function(){
            if(this.width != 1 || this.height != 1) {
                that.dataURI = false;
            } else {
                that.dataURI = true;
            }
            if (that.onDataURI)
                that.onDataURI();
        }
        data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
    }
};

var mySupport = new Support();
mySupport.onDataURI = function() { alert(mySupport.dataURI); }
mySupport.getDataURI();
2 голосов
/ 23 декабря 2010

Вы не можете ссылаться на объект при объявлении его.

Вместо этого вам нужно вызвать вашу функцию после объявления объекта:

var Support = { 
    ...
};

(function() {
    var data = new Image();
    data.onload = data.onerror = function(){
        Support.dataURI = this.width === 1 && this.height === 1;
    };
    data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
})();
0 голосов
/ 23 декабря 2010

Полагаю, вы должны использовать опору вместо этого в своей функции. (Вы вызываете функцию, передавая this в качестве параметра для аргумента prop - но внутри функции вы используете «this»?).

dataURI : function(prop) {
    var data = new Image();
    data.onload = data.onerror = function(){
        if(prop.width != 1 || prop.height != 1) {
            that = false;
        }
        that = true;
    }
    data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
    return -1;
}(this);
...