1

我希望能够将一堆不同变量的地址存储在一个数组中。这使我可以按名称访问变量或在需要时遍历它们。这在JS中可能吗?

(function(ns){

    ns.obj = new function(){

        var foo = "foo";
        var bar = "bar";

        //i really want this:
        //var ary = [&foo, &bar];
        var ary = [foo, bar];


        this.print = function() {
            console.log( foo );
            console.log( bar );
        }

        this.setFoo = function( newFoo ) {
            //i really want this:
            //*(ary[0]) = newFoo;
            ary[0] = newFoo;
        }

        this.printAry = function() {
            for( var i=0; i < ary.length; ++i ) {
                console.log( ary[i] );
            }
        }
    };

}(window.ns = window.ns || {}) );

ns.obj.print();
ns.obj.setFoo("newfoo!");
ns.obj.printAry();
ns.obj.print();

我看着这个:

JavaScript 指针数组,如 C++

但我希望能够ary在作业的 LHS 上使用一个元素,我认为该示例在这种情况下不起作用。

我到底为什么要这样做?

到目前为止,很多评论(正确地)问我为什么要这样做。我正在处理一个涉及异步对象初始化机制的专有 API。基本上我创建一个对象的实例,然后将它传递给这个初始化程序以便能够实际使用它。初始化程序包括一个用于通知成功初始化的 onSuccess 处理程序的字段。我完全初始化的对象作为参数传递给这个成功处理程序,以便我可以获取对它的引用。

然后我可以自由地初始化我的下一个对象。它看起来有点像这样:

     var a = new api.ApiObject();
     var b = new api.ApiObject();
     var c = new api.ApiObject();
     var d = new api.ApiObject();

     //omg this is ugly
     api.initializeObject( {
         objToInit: a,
         onSuccess: function(args) {
             a = args.obj;
             api.initializeObject( {
                 objToInit: b,
                 onSuccess: function(args) {
                     b = args.obj;
                     api.initializeObject( {
                         objToInit: c,
                         onSuccess: function(args) {
                             c = args.obj;
                             api.initializeObject( {
                                 objToInit: d,
                                 onSuccess: function(args) {
                                     d = args.obj;
                                 }
                             } );
                         }
                     } );
                 }
             } );
        }
    } );

    a.doCoolStuff();
    //and so on

随着我添加更多,这种深层嵌套的混乱只会变得更糟api.ApiObjects()。那么我该怎么做才能解决这个问题呢?我无法更改 API,但也许递归函数会有所帮助:

    //maybe a recursive function could make this more concise?
    function doInitialize( ary ) {
        api.initializeObject( {
            objToInit: ary[0];
            onSuccess: function(args) {
                //i'd like to assign this passed in reference to my local
                //reference outside this function (var a, b, etc).  
                //An array of pointers would be useful here.
                //how else can I get this assigned out, cuz this doesn't work...
                ary[0] = args.obj;
                if( ary.length > 1 ) {
                    ary.splice( 0, 1 );
                    doInitialize( ary );
                }
            }
        }
    }

    doInitialize( [a,b,c,d] );

    //this won't work because I don't have a reference to the fully initialized object
    a.doCoolStuff();

所以也许更好的问题是:是否有一个既定的模式来处理这样的异步成功链接?我想我已经看到其他公共 JS 框架(如 dojo)使用这种 onSuccess 链接......我怎样才能让它不丑?

4

2 回答 2

2

我可能会建议,如果您这样做的主要目的是为了方便异步回调的嵌套,那么您应该考虑使用延迟/承诺系统。

我手工编写了几个不同的 Promise 库。
jQuery 带有一个内置的(就像大多数“ajax 库”一样)。

在一个更好的世界中,这可能是这样的:

doThingOne()
    .then(doThingTwo)
    .then(doThingThree)
    .then(launch);

假设doThingOne返回一个承诺。

对于使用 jQuery(或大多数其他使用承诺的大型库)的人来说,一个看起来更熟悉的界面可能如下所示:

var imageLoader = $.Deferred(),
    loading     = imageLoader.promise();

loading
    .done(gallery.render.bind(gallery))
    .done(gallery.show.bind(gallery));

var img = new Image(),
    url = "...";

img.onload  = function () { imageLoader.resolve(img); };
img.onerror = function () { imageLoader.reject("error message"); };
img.src = url;

基本上,Deferred上面将包含两个私有数组(一个用于“成功”,一个用于“失败”),并将扩展一个接口,允许应用程序的异步部分“成功”或“失败”,并将传入任何被选为数据/回调/等的东西。

它还扩展了一个promise方法,该方法返回一个 promise 对象,其中包含两个私有数组的订阅函数。因此,您将 Promise 对象传递给感兴趣的各方,并且他们订阅回调以在异步操作的成功/失败时进行迭代(并传递任何传递给.resolve/.reject操作方法的内容)。

这似乎只是添加自定义事件/侦听器/等的反转或扩展......确实如此。

抽象的好处是接口更干净。

将这些东西隐藏在对象接口中,并且只传递异步承诺对象可以使您的代码看起来 100% 同步:

var images  = ImageLoader(),
    gallery = ImageGallery(),
    photo;


photo = images.load("//url.com/image.png"); // assuming `.load` returns a promise object
gallery.show(photo); // just a promise object, but internally,
                     //`.show` would subscribe a private method to the promise object

并且执行诸如具有三个单独的异步操作之类的事情,它们可以以任何顺序到达,但在推进之前必须全部成功,然后您可以拥有这样的事情(同样是 jQuery,但也可以手动完成)。

$.when(promise_obj_1, promise_obj_2, promise_obj_3)
.done(nextPhase);

nextPhase 当然是一个回调,如果所有三个 Promise 都成功完成,您预计会触发它。

我很乐意为准系统承诺系统提供实现细节,如果你和我一样,并且不喜欢使用不同的库而不首先了解每个部分是如何独立工作的,并且能够复制它的功能,没有复制代码。

于 2013-03-13T06:55:37.580 回答
1

问题第一部分的答案是使用对象。您正在考虑 C 中没有可迭代的结构,因此 C 程序员可以使用数组。在 JS 中,对象是可迭代的。所以你应该把它写成:

ary = { foo : 'foo', bar : 'bar' }

或者,如果我们看一下您的第二个示例:

var apis = {
    a : new api.ApiObject(),
    b : new api.ApiObject(),
    c : new api.ApiObject(),
    d : new api.ApiObject()
}

现在,至于你问题的第二部分。您的伪递归代码(伪,因为它在堆栈意义上不是真正的递归,因为它是异步的)现在可以与apis上面的对象一起使用。但是您传递的是键而不是对象:

doInitialize( ['a','b','c','d'] );

显然,上面的位可以通过遍历 apis 对象来动态完成。无论如何,在onSuccess您分配结果的代码部分中,如下所示:

apis[ary[0]] = args.obj;

哦,显然objToInit现在应该是apis[ary[0]]

现在这样做应该可以按您的预期工作:

apis.a.doCoolStuff();
于 2013-03-13T07:22:41.527 回答