简体   繁体   English

我可以在javascript中模拟一个类似C的指针数组吗?

[英]can I emulate a C-like array of pointers in javascript?

I'd like to be able to store the addresses of a bunch of different variables in an array. 我希望能够将一堆不同变量的地址存储在一个数组中。 This allows me to access the variables by name or iterate through them if I need to. 这允许我按名称访问变量或者在需要时迭代它们。 Is this possible in JS? 这可能在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();

I looked at this: 我看着这个:

JavaScript array of pointers like in C++ 像C ++一样的JavaScript数组指针

But I'd like to be able to use an element of ary on the LHS of an assignment and I don't think that example works in this situation. 但我希望能够在作业的LHS上使用ary元素,我不认为这个例子适用于这种情况。

WHY ON EARTH DO I WANT TO DO THIS? 我为什么要去做这个?

A lot of comments so far have (rightfully) asked why I'd want to do this. 到目前为止,很多评论都(正确地)问我为什么要这样做。 I'm dealing with a proprietary API that involves an asynchronous object initialization mechanism. 我正在处理涉及异步对象初始化机制的专有API。 Basically I create an instance of an object and then pass it to this initializer to be able to actually use it. 基本上我创建一个对象的实例,然后将它传递给这个初始化器,以便能够实际使用它。 The initializer includes a field for an onSuccess handler to notify of successful initialization. 初始化程序包括onSuccess处理程序的字段,用于通知成功初始化。 My fully initialized object is passed as an argument into this success handler so that I can grab a reference to it. 我的完全初始化对象作为参数传递给此成功处理程序,以便我可以获取对它的引用。

I'm then free to initialize my next object. 然后我可以自由地初始化我的下一个对象。 It looks kinda like this: 看起来有点像这样:

     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

This deeply nested mess just gets worse as I add more api.ApiObjects() . 当我添加更多api.ApiObjects()这种深度嵌套的混乱变得更糟。 So what do I do to fix this? 那么我该怎么做才能解决这个问题呢? I can't change the API, but maybe a recursive function could help: 我无法更改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();

So maybe the better question is: is there an established pattern to deal with asynchronous success chaining like this? 所以也许更好的问题是:是否有一个既定的模式来处理这样的异步成功链接? I think I've seen other public JS frameworks (like dojo) use this sort of onSuccess chaining... how do I make this not ugly? 我想我已经看到其他公共JS框架(比如dojo)使用这种onSuccess链接......我怎么能让它变得不丑?

I might suggest that if your primary purpose for this is convenience as regards nesting of asynchronous callbacks, that you should consider a deferred/promise system. 我可能会建议,如果你的主要目的是为了便于嵌套异步回调,你应该考虑延迟/保证系统。

I've written a couple of different promise libraries by hand. 我已经手工编写了几个不同的promise库。
jQuery comes with one built in (as do most "ajax libraries"). jQuery带有一个内置的(和大多数“ajax库”一样)。

Here's what this might look like, in a better world: 在一个更美好的世界里,这可能是这样的:

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

Assuming that doThingOne returns a promise. 假设doThingOne返回一个promise。

A more familiar looking interface for people who use jQuery (or most other promise-using large libraries), might look like this: 对于使用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;

Very basically, the Deferred above will hold two private arrays (one for "success", one for "failure"), and will extend an interface which allows the async part of the application to "succeed" or "fail", and will pass in whatever is chosen to be data/a callback/etc. 基本上, Deferred上面将保存两个私有数组(一个用于“成功”,一个用于“失败”),并将扩展一个接口,允许应用程序的异步部分“成功”或“失败”,并将通过无论选择什么样的数据/回调/等等。

It also extends a promise method, which returns a promise object, containing subscription functions for the two private arrays. 它还扩展了一个promise方法,它返回一个promise对象,包含两个私有数组的订阅函数。 So you pass the promise object around to interested parties, and they subscribe callbacks to be iterated through, on success/failure of the async operation (and passed anything which is passed to the .resolve / .reject method of the operation). 因此,您将promise对象传递给感兴趣的各方,并且他们在异步操作成功/失败时订阅要回调的回调(并传递传递给操作的.resolve / .reject方法的任何内容)。

This might seem like an inversion or extension of just adding a custom-event/listener/etc... And it is. 这似乎只是添加自定义事件/侦听器/等的反转或扩展...而且确实如此。

The benefit of the abstraction is that the interface is cleaner. 抽象的好处是界面更清晰。

Hiding this stuff inside of object interfaces, and just passing async promise-objects around can make your code look 100% synchronous: 将这些东西隐藏在对象接口中,只是传递异步promise对象可以使你的代码看起来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

And doing things like having three separate async operations, which can arrive in any order, but must all be successful before advancing, then you can have something like this (again jQuery, but doing it by hand is possible, too). 做三个单独的异步操作,可以按任何顺序到达,但必须在推进之前都成功,然后你可以有这样的东西(再次jQuery,但也可以手工完成)。

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

nextPhase, of course, being a callback which you anticipate to be fired if all three promises are successfully completed. nextPhase,当然是一个回调,如果所有三个承诺都成功完成,你预计会被解雇。

I'd be happy to provide implementation details for a barebones promise system, if you're like me, and don't like using different libraries without first understanding how each piece works on its own, and being able to replicate its functionality, without copying code. 我很乐意提供准系统承诺系统的实现细节,如果你像我一样,并且不喜欢使用不同的库而不先了解每个部分如何独立工作,并且能够复制其功能,复制代码。

The answer to the first part of your question is to use an object. 问题第一部分的答案是使用一个对象。 You're thinking in C which doesn't have iteratable structs so C programmers reach for arrays. 您正在考虑C中没有可迭代结构,因此C程序员可以使用数组。 In JS objects are iteratable. 在JS中,对象是可迭代的。 So you should write it as: 所以你应该把它写成:

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

Or if we look at your second example: 或者,如果我们看看你的第二个例子:

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

Now, as for the second part of your question. 现在,至于问题的第二部分。 Your pseudo recursive code (pseudo because it's not really recursive in the stack sense since it's async) will now work with the apis object above. 您的伪递归代码(伪因为它在堆栈意义上不是真正的递归,因为它是异步的)现在将与上面的apis对象一起使用。 But you pass the keys instead of the object: 但是你传递的是键而不是对象:

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

Obviously, the bit above can be done dynamically by iterating through the apis object. 显然,上面的位可以通过遍历apis对象动态完成。 Anyway, in the onSuccess part of the code you assign the result like this: 无论如何,在代码的onSuccess部分,你分配如下结果:

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

Oh, and obviously the objToInit should now be apis[ary[0]] . 哦,显然objToInit现在应该是apis[ary[0]]

Now doing this should work as you expect: 现在这样做应该按预期工作:

apis.a.doCoolStuff();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM