繁体   English   中英

如何封装和创建可维护的JavaScript

[英]How to Encapsulate and Create Maintainable JavaScript

我遇到了一个有趣的挑战,我有以下代码:

Sm.screenfragment(function (screen, viewModel) {
    //This can grow very quickly and turn into a mess
    var attribA = screen.get('title'),
        ... 
        ... 
        attribZ = screen.get('status');

    var setAttribA = function (container) {
        //Do Stuff
    };
    ...
    ...
    var setAttribZ = function(event, viewName) {
        //Do Stuff
    };

    //So this can grow hundreads of lines and get messy.
    return {
        model: {
            //Do Stuff
        },
        create: function () {
            //Do Stuff
        },
        prepare: function (callback, config) {
            //Do Stuff
        },
        enter: function () {
            //Do Stuff
        },
        exit: function (callback) {
            //Do Stuff
        }
    };
});

我尝试了一些想法,但仅是弄乱了语法:

我考虑过要添加一个新的util对象,因为我可以构造util对象,所以只增加了一点结构,但没有更多。

Sm.screenfragment(function (screen, viewModel) {
    //Still can grow into a mess
    var util = {
            attribA : screen.get('title'),
            attribB : screen.get('status'),
            setAttribA : function (container) {
            //Do Stuff
            },
            setAttribB : function(event, viewName) {
            //Do Stuff
            }   
    };

    return {
        model: {
            //Do Stuff
        },
        create: function () {
            //Do Stuff
            util.setAttribA...
        },
        prepare: function (callback, config) {
            //Do Stuff
        },
        enter: function () {
            //Do Stuff
        },
        exit: function (callback) {
            //Do Stuff
        }
    };
});

然后使用点表示法获取属性,但这不会使混乱消失。 我正在重新阅读“模块模式”中的内容,以查看是否可以在此处应用某些内容,在极端情况下,我可以在文件顶部包含数十个专有和函数,这只会破坏结构。 如何以更模块化的方式排列代码? 这样就不会混乱。

您的问题无法轻易回答,因为缺少某些信息。 哪些方法使用哪个属性? 分析问题时应该解决此问题(不在设计中,而且绝对不在代码中)。 那么,您希望将代码的哪一部分与另一部分分离?

要更一般地回答这个问题:

如果您在一个模块内实现了很高的内聚性并且模块之间的耦合性很低,那么这被认为是良好的面向对象设计(OOD)。 在您的情况下,这意味着:如果所有方法都引用了所有属性,则将所有属性都保存在一个大文件中被认为是好的OOD。 但是通常情况下,现实世界中的问题并不是以这种单一的方式进行的。

如果您想使某事脱钩,则有一个单一责任原则说明,即应该使不相互影响的零件脱钩。 在您的情况下,您可以(也许)将有关attribA所有内容放入一个模块,并将有关attribB所有内容放入另一个模块。 因此,无论您使用什么具体的模块实现,它都不会弄乱。

好吧,我至少在可读性方面采用的方法是将函数声明从return块中拉出:

Sm.screenfragment(function (screen, viewModel) {
    //Still can grow into a mess
    var util = {
            attribA : screen.get('title'),
            attribB : screen.get('status'),
            setAttribA : function (container) {
            //Do Stuff
            },
            setAttribB : function(event, viewName) {
            //Do Stuff
            }   
    };

    var create = function() {
        //Do Stuff
        util.setAttribA...
    };

    var prepare = function(callback, config) {
        //Do Stuff
    };

    var enter = function() {
        //Do Stuff
    };

    var exit = function(callback) {
        //Do Stuff
    };

    return {
        model: {
            //Do Stuff
        },
        create: create,
        prepare: prepare,
        enter: enter,
        exit: exit
    };
});

然后,如果这些函数中的代码是通用/模块化的,则将该代码提取到实用程序文件中,然后从那里调用它们

我@Waog同意,良好的OOD可能是解决您的问题。 鉴于您正在为setAttribA函数编写对象的对象上具有许多属性,为什么不使用映射稍微清理一下代码? 可能有数十种JavaScript的地图实现,一个简单的Google搜索“ map polyfill ”使我想到了这一点,看起来很不错: http : //eriwen.github.io/smap.js/

似乎有机会使用OO方法封装类似的功能。 每个类都可以存在于其自己的文件中……看起来不错:

// In file attrs.js
function ScreenAttrs(screen) {
    this.screen = screen;
    this.attribA = screen.get('title');
    // ...
    // ... could even categorize attributes into separate methods
    // this.initFooAttrs();
    // this.initBarAttrs();
};

// In file modifier.js
var attrs = new ScreenAttrs(screen);

function AttribModifier(attributes) {
    this.attrs = attributes;
};

AttribModifier.prototype.setAttribA = function() {
    // .. do stuff w/ this.attrs.attribA
};
// ... etc.

// in api.js
var modifer = new AttribModifier(attrs);

function ScreenAPIImpl (modifier) {
    this.modifier = modifier;
};

ScreenAPIImpl.proxy = function(context, method) {
    return function() {
        return method.apply( context, args.concat( slice.call( arguments ) ) );
    };
};

ScreenAPIImpl.prototype.model = function (foo) {
    // operation with this.modifier.setAttribA
    // operation with this.modifier.attrs.attribA
};

ScreenAPIImpl.prototype.fetchAPI = function (screen, viewModel) {
    return {
        // Bind context of method to this object instance
        model: this.proxy(this, this.model),
        // ...
    };
};

// .. etc

var api = new ScreenAPIImpl(modifier);

Sm.screenfragment(api.fetchAPI(screen, viewModel));

这也使自己可以创建一个辅助构建器类,该类构造所有内容并返回最终的API对象:

var api = CreateAPI(screen);

让我们考虑一下您的代码中的以下摘录,据我了解,问题所在:

Sm.screenfragment(function (screen, viewModel) {
    //This can grow very quickly and turn into a mess
    var attribA = screen.get('title'),
    ... 
    ... 
    attribZ = screen.get('status');

    var setAttribA = function (container) {
        //Do Stuff
    };
    ...
    ...
    var setAttribZ = function(event, viewName) {
        //Do Stuff
    };

    ...

据我所知,我认为没有必要定义属性attribAattribZ ,然后进行设置,然后再为它们定义setter函数。 您只需在需要的位置和时间返回或访问screen.get('x')

但是,由于某些原因,如果绝对必要,那么以下由jQuery推广的策略就足够了:

attributeX = function(x, container){
    // if x and container are undefined, then we can assume API caller intends to
    // read value of property 'x' otherwise, caller intends to write to the property.
    if(container){
        // don't forget to do stuff before setting!
        container.setProp(x); // you get the idea
    } else {
        // well we must have a 'container' to set a prop,
        // if we don't then caller must want to read.
        return screen.get(x);
    }
}

如果此策略不能解决问题,或者您认为我对问题的理解不正确,请尝试使情况更清楚,使我们更接近目标。

暂无
暂无

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

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