JavaScript中的动态服务定位器?

时间:2013-01-22 04:23:37

标签: javascript node.js service-locator proxies ecmascript-harmony

我正在尝试使用Harmony Proxies(Node.js)获取用JavaScript编写的动态服务定位器。基本上你会创建一个新的容器:

 var container = new Container();

然后,您就可以像传统服务定位器一样设置和获取值:

 container.set('FM', {});
 container.get('FM');
 container.get('FM', function(FM) {

 });

您甚至可以拥有与子对象相似的名称空间:

 container.set('FM.Object', {});

问题在于动态别名。

 var App = container.alias('App');

创建别名时,它会创建一个新的代理对象,您可以像传统对象一样对其进行操作,但它是get()set()方法的别名。

而不是:

 container.get('App.Hello'); 

使用别名,您可以使用:

 App.Hello

问题在于深层命名空间。假设您正在尝试访问App.Hello.World.Controller,因为它一次通过代理一个命名空间而不是一次完成(就像完整的命名空间一样)。我如何知道用户是否正在调用App.Hello来检索存储在服务定位器中的值,或者用户是否想要继续访问更深的命名空间?你不能(从我试过的)。

您可以通过其他方式实现此目标吗?

语法App.Hello.World将返回代理,如果它将访问更深的命名空间,或存储在服务定位器中的值。

  var App = container.alias('App');
  App.Controller.Home.Method.Index
  // It would go through:
  App -> Proxy
  App.Controller -> Proxy or Value?
  App.Controller.Home -> Proxy or Value?
  App.Controller.Home.Method -> Proxy or Value?
  App.Controller.Home.Method.Index -> Proxy or Value?

现在,我已经组建了一个处理这两个选项的约定。您将使用单数名称来检索值,并使用复数来返回代理。这只是一个快速的“黑客”,因为它不是很有效。

(如果您需要澄清或更多信息,请告诉我)

1 个答案:

答案 0 :(得分:3)

问题概述

让我看看我是否理解正确。听起来您希望“一起”访问的属性被视为controller.get的单个键的一部分。请看以下示例:

controller.set('App.Foo.Bar', { Zap: 1 });
controller.set('App.Foo.Bar.Zap', 2);

var App = controller.alias('App');

var Bar = App.Foo.Bar;
var Zap = Bar.Zap;

var Zap2 = App.Foo.Bar.Zap;

console.log(Zap, Zap2);
// should log: 1, 2

如果上述行为符合您的要求,那么就不可能(使用该语法)。 JavaScript不区分App.Foo.Bar.Zapvar Bar = App.Foo.Bar; Bar.Zap。语言中没有任何内容可以区分“一起”访问的属性(var Bar = App.Foo.Bar)和“单独”访问的属性(Bar.Zap)。


提案

但是,您可以通过修改语法来接近。我想到了几种不同的可能性,这是我能想到的最好的:

controller.set('App.Foo.Bar', { Zap: 1 });
controller.set('App.Foo.Bar.Zap', 2);

var App = controller.alias('App');

// Use parens to note that we are ending the key name
// and want it to retrieve a value.
var Bar = App.Foo.Bar();
var Zap = Bar.Zap;

var Zap2 = App.Foo.Bar.Zap();

console.log(Zap, Zap2);
// should log: 1, 2

如果在属性检索后添加括号,如上所述,那么您可以使用此语法作为代理的区别注释,这意味着从控制器检索值。


实施

以下是使这成为可能的代码:

var Container = (function() {

    return function Container() {

        var map = { };

        this.get = function get(key) {
            return map[key];
        };

        this.set = function set(key, value) {
            map[key] = value;
        };

        this.alias = function alias(name) {
            return createAlias(this, name);
        };

    };

    function createAlias(container, aliasName) {
        return new Proxy(
            function() {
                return container.get(aliasName);
            },
            {
                get: function(target, name) {
                    return createAlias(container, aliasName + '.' + name);
                },
                set: function(target, name, value) {
                    container.set(aliasName + '.' + name, value);
                }
            }
        );
    }

})();

您可以使用=进行设置,然后使用()进行检索:

var container = new Container();
container.set('App.Controller.Home.Method.Index', 5);

var App = container.alias('App');

App.Controller.Home.Method = 7;

console.log(
    App.Controller.Home.Method(),      // => 7
    App.Controller.Home.Method.Index() // => 5
);

最后的注释

我确信你知道Proxies是实验性的,但我只想强调ES6的这个特殊功能仍然很不稳定。可能是当前的直接代理提案最终被包含在语言中,但ES中的代理也很可能在明年发生重大变化。 (在过去的一个月中,关于代理提案的ECMAScript邮件列表已经进行了激烈的讨论。)至少对于Node,如果代理发生显着变化(当你更新旧的实现时),你可以继续使用旧版本的Node。

希望这有帮助!