通过异步回调设置对象文字属性值。

发布于 2024-10-09 03:47:12 字数 1815 浏览 5 评论 0原文

我正在创建一个独立的 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)
};

我立即执行匿名函数,传递 this (我希望它是对 Support.dataURI 的引用),但不幸的是引用了 window 对象 - 因此该值始终为 -1。我可以通过使用外部定义的函数在创建支持对象后分配值来使其工作......但我认为这种方式不是很干净。有没有办法让它独立?对象字面量的函数可以引用它所分配的属性吗?

编辑 -----------------------------------
不完全是我的问题的答案(如措辞),所以我不会发布额外的答案,但是......我决定使用单例对象而不是对象文字。这是工作代码:

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;
    }();
};

I'm creating a self-contained javascript utility object that detects advanced browser features. Ideally, my object would look something like this:

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

My current problem deals with some code I'm adapting from Weston Ruter's site which detects dataURI support. It attempts to use javascript to create an image with a dataURI source, and uses onload/onerror callbacks to check the width/height. Since onload is asynchronous, I lose my scope and returning true/false does not assign true/false to my object.

Here is my attempt:

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)
};

I'm executing the anonymous function immediately, passing this (which I hoped was a reference to Support.dataURI), but unfortunately references the window object -- so the value is always -1. I can get it to work by using an externally defined function to assign the value after the Support object is created... but I don't think it's very clean that way. Is there a way for it to be self-contained? Can the object literal's function reference the property it's assigned to?

EDIT -----------------------------------
Not exactly the answer to my question (as phrased) so I'm not going to post an additional answer, BUT... I've decided to use a singleton object instead of an object literal. Here is the working code:

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;
    }();
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

满地尘埃落定 2024-10-16 03:47:12

对象字面量的函数可以引用它所分配的属性吗?

是的,可以。每个函数都有一个本地参数变量,该变量有一个被调用者引用被调用函数的 属性。因此,如果您想引用 Support.dataURI,您可以简单地使用它:

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

但是,我认为这并不是您真正想要的。看起来您正在尝试引用属性名称,而不是值 - 在这种情况下,您的答案是否定的。 JavaScript 方法无法知道它存储在哪些属性中;事实上,它可以同时存储在多个属性中。例如:

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

这里,Support 有两个属性,它们都指向同一个方法。然而,该方法本身无法知道调用了哪个引用。由于 Support 对象尚未声明,因此您无法引用它。

事实上(尽管您询问“对象文字的函数是否可以引用它分配给的属性”),该函数甚至不存储在对象上:它的结果是。因此该方法本身与 Support 没有任何关联。

恐怕您最好声明该对象,然后设置其 dataURI 属性。


回复您的编辑

首先,您不需要在“function”之前使用“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==";

};

但请记住:仍然不能保证(至少据我所知)onloadonerror 处理程序将在您设置来源——事实上,甚至可以保证他们不会!因此,您可能会向 Support 对象添加回调,并在设置 dataURI 后执行它。不过,然后,您必须防止 onloadonerror 同步执行的可能性。像这样的东西应该有效:

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();

Can the object literal's function reference the property it's assigned to?

Yes, it can. Every function has a local argumentsvariable, which has a callee property that references the called function. So if you wanted to reference Support.dataURI, you could simply use that:

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

However, I don't think that's really what you want. It looks like you're trying to reference the property name, not the value—in which case your answer is no. A JavaScript method has no way of knowing what properties it's being stored in; indeed it can be stored in multiple properties simultaneously. For example:

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

Here, Support has two properties that both point to the same method. The method itself, however, has no way of knowing which reference was called. And since the Support object has yet to be declared, you have no way of referencing it.

In fact (though you ask if the "object literal's function [can] reference the property it's assigned to"), the function isn't even stored on the object: its result is. So the method itself isn't associated with Support in any way.

I'm afraid the best you can do it declare the object, then set its dataURI property.


In Response to Your Edit

First of all, you don't need the "new" keyword before "function". Still, I don't see how this could work in any browser. If you simplify it, lines 3-14 of your code have the form `this.dataURI = fn();'. Here's how a statement like this works:

  1. The value is determined by executing fn()
  2. The value is assigned to the dataURI property.

At the point that fn() is being executed, dataURI hasn't yet been assigned, so there's no way to access its (correct) value to return it.

In addition, you have an (at least potentially) asynchronous process (waiting for the image to load or error), so the value won't even have been set by the callback. Basically, you're trying to wrap an asynchronous process (onload event) in a synchronous one (function call).

Finally, there's no need for you to create and execute a function here. Just get rid of it:

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==";

};

But remember: there's still no guarantee (that I know of, at least) that the onload and onerror handlers will fire synchronously (immediately) when you set the source—in fact, there may even be a guarantee that they won't! So you're likely going to add a callback to your Support object, and execute it once dataURI is set. Then, though, you'll have to guard against the possibility that onload or onerror is executed synchronously. Something like this should work:

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();
醉殇 2024-10-16 03:47:12

声明对象时不能引用该对象。

相反,您需要在声明对象后调用函数:

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==";
})();

You cannot reference an object while declaring it.

Instead, you need to call your function after declaring the object:

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==";
})();
断肠人 2024-10-16 03:47:12

我想你应该在你的函数中使用 prop 而不是 this 。 (您调用将 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);

I guess you should use prop instead of this within your function. (You call the function passing this as a parameter for the argument prop - but within the function you use "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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文