3

我正在尝试使用 Javascript 来模拟 Java 的以下行为mootools/Dojo。需要使用类静态变量值初始化的实例变量。

Class xyz {
    public static static_constants {
        TEST_CONST1 : "abc";
    }

    private a = static_constants.TEST_CONST1;
}

我可以按照下面的方式进行操作,但是com.example.test.static_constants.TEST_CONST1因为我们已经覆盖了com.example.test. 但是如果com.example.test.static_constants.TEST_CONST1之前没有定义,那么类中的赋值就会失败。

com.example.test.static_constants.TEST_CONST1 = "abc";

var com.example.test = new Class ({
    a : com.example.test.static_constants.TEST_CONST1
});

两种有效的解决方法:

(I) - 在类中有单独的实例变量;并在外面使用静态

var com.example.test = new Class ({
    static_constants : {
        TEST_CONST1 : "abc"
    },
    a : this.static_constants.TEST_CONST1
});

com.example.test.static_constants.TEST_CONST1 = "abc";

(二)类定义后重新创建静态

com.example.test.static_constants.TEST_CONST1 = "abc";

var com.example.test = new Class ({
    a : com.example.test.static_constants.TEST_CONST1
});

com.example.test.static_constants.TEST_CONST1 = "abc";

我错过了什么?这两种方式对我来说似乎都不干净。必须有一种更简洁的方法来做到这一点(不使用扩展等 - 这将进一步破坏课程)。

4

3 回答 3

3

我可以插话并阻止你。

JavaScript 不是 JAVA。您现在不需要使用 JAVA 的事实并不意味着您需要将与命名空间、静态和模式相关的废话转移到 JavaScript。JavaScript 在做事时有不同的模式。

在任何情况下,由于您没有询问在 MooTools 中组织内容和类的最佳模式是什么,并且您只想知道如何改变和扩展现有对象,您可以做这个小工厂:

http://jsfiddle.net/vrKX2/4/

var com = {
    example: {
        test: {
            static_constants: {
                TEST_CONST1: "abc"
            }
        }
    }
};

Class.extend({
    fromExisting: function(namespace, constructorObject){
        return Object.append(new this(constructorObject), namespace);
    }
});

// this is non-DRY. It kind of is Object.merge()
com.example.test = Class.fromExisting(com.example.test, {

    a: com.example.test.static_constants.TEST_CONST1,
    initialize: function(){
        console.warn(this.a);
    },
    getStatic: function(what){
        return com.example.test.static_constants[what];
    }
});

var t = new com.example.test();
console.log(Object.getOwnPropertyNames(t)); // no static_constants
console.log(t.a);
console.log(com.example.test.static_constants);
com.example.test.static_constants.TEST_CONST1 = 'bbb';
console.log(t.a);
console.log(t.getStatic('TEST_CONST1'));

Class.fromExisting将接受一些对象,它将其属性与类构造函数对象合并(但是静态的,而不是在原型上) - 但它不能改变它在构造函数中扩展的 obj(现在想不出为什么,但它是星期六夜晚)。

你真的应该实现一个更明智的模块命名方法/命名空间 - 就像在 AMD 中一样,并constants在单独的配置对象上使用。没有必要混合这两个命名空间——或者实际上,当你使用 AMD 时,根本不需要命名空间(尽管你可以命名你的模块)。

请不要使用这种工厂模式,只是展示了 mootools 在扩展类型等时的灵活性。

相反,您可以简单地做 - 如前所述 - 只是Object.merge()

com.example.test = Object.merge(new Class({
    a: com.example.test.static_constants.TEST_CONST1,
    initialize: function(){
        console.warn(this.a);
    },
    getStatic: function(what){
        return com.example.test.static_constants[what];
    }
}), com.example.test);

由于您正在使用一些不可靠的命名空间,因此您想查看使用深入工作的 get/set 扩展对象类型(不是对象的原型),Daniel Buchner 试图登陆它但被击落。

https://github.com/mootools/mootools-core/pull/2191/files

这些可能会消除从虚线创建更深的命名空间的痛苦。现在玩得开心。

于 2013-11-08T20:17:09.600 回答
1

首先,我将您的问题分解为两个单独的问题:

  1. 如何从静态属性(常量或其他)设置实例属性
  2. 如何创建方便的常量属性(无论是实例化的还是静态的)

由于 JS 和 Java 之间的架构差异,无论你做什么,它都会有点难看。


静态属性

这里的关键是您必须延迟访问静态变量的尝试,直到实际创建实例。我不知道 mootools(是initialize吗?),但在 Dojo 1.9 中,相关方法被称为constructor,所以这就是我将在本示例中使用的方法:

var makeMyClass = function(){

    var clazz = new Class ({
        instanceVal : null, // Don't know what to put here yet

        constructor : function(){
            /* 
            I know this looks weird that this property is ALSO 
            called "constructor", but it works in Dojo 1.9. Consult
            your debugger to see if a similar tactic works in mootools.
            */
            var selfClass = this.constructor; 
            
            // Remember that if staticVal is an object, you may-or-may-not want an independent copy
            this.instanceVal = selfClass.staticVal;
        }
    });

    clazz.staticVal = "Hello World";
    return clazz;
};
    
var com.example.myClass = makeMyClass();

现在,如果您修改com.example.myClass.staticScalarVal它应该会影响将来构建的类,尽管 IMO 构建器或工厂设计模式会好得多。


常数

这里的实现很大程度上取决于你想变得多么僵硬/偏执,因为 JS 自然对动态变化非常宽容。所以这里只是一些开始的想法:

由访问器方法保护的常量

如果您想在访问未定义的常量时抛出错误(而不是undefined在 15 步后返回并使其失败),请考虑:

var createLightweightConstantHolder = function(){
    
    // Because it's nestled in this method, we're protected from the evil outside world.
    var constants  = {        
        FOO : "hello",
        BAR : [1,2,3]
    };
    
    return function(cname){
        if(typeof cname == "undefined"){
            // Gives all constants back at once, in case someone needs to iterate
            return lang.clone(constants);
        }else{
            // Value of a particular thing
            if(constants.hasOwnProperty(cname)){
                return constants[cname];
            }else{
                throw "Constant '"+cname+"' does not exist.";
            }
        }
    }
};

var stuff = createLightweightConstantHolder();

stuff("FOO"); // Returns "hello"
stuff("BAZ"); // Throws a descriptive exception 
var copyOfUnderlyingValues = stuff();

这有点难看,因为您将常量名称作为字符串传递,但它确实会快速失败并阻止修改。

常量使用Object.freeze

这一个使用“冻结”对象的相对较新的功能。请注意,它仅冻结顶级对象以防止添加/删除/替换值。仍然可以更改非标量。

var constants  = {        
    FOO : "hello",
    BAR : [1,2,3],
    BAZ : {"x","y"}
};

constants = Object.freeze(constants);

/*
 *  Some things which do/don't work: 
 */
constants.FOO; // Is "hello"

constants.FOO = "goodbye"; // Won't change. Depending on env and strict-mode, may throw an error or else silently fail.

constants.OOPS; // Is undefined, does NOT throw an error when access it.

constants.BAR.push(4); // DOES alter array items

constants.BAZ.x = "z"; // DOES alter object properties

这并非在所有平台上都可用,请查看浏览器/环境兼容性图表。

于 2013-11-08T17:58:49.387 回答
1

如果您使用的是 AMD,您将使用define一个模块而不创建像com.example.test. 的文档declare一个关于静态对象的简短部分,但概念很简单:您可以将“附加”挂在您返回的模块上。

如果您需要一个仅对定义它的模块可用的常量,只需使用常规变量即可。如果您需要在模块之外访问它,只需将其挂在您返回的模块对象上即可。这类似于您问题中的第一种方法,但不需要涉及丑陋的全局对象链。

于 2013-11-08T17:04:27.107 回答