如何封装和创建可维护的JavaScript
我遇到了一个有趣的挑战,我有以下代码:如何封装和创建可维护的JavaScript,javascript,design-patterns,Javascript,Design Patterns,我遇到了一个有趣的挑战,我有以下代码: 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 =
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
的所有内容放在另一个模块中。因此,无论您使用的是哪种具体的模块实现,它都不会变得一团糟。您的问题不能那么容易回答,因为缺少一些信息。哪些方法使用哪些属性?这是一个在分析问题时应该解决的问题(不是在设计中,也肯定不是在代码中)。那么,您的代码的哪一部分需要与哪一部分解耦呢
要从更一般的层面回答问题,请执行以下操作:
如果您在一个模块内部实现了高内聚,模块之间实现了低耦合,那么它被认为是良好的面向对象设计(OOD)。在您的例子中,这意味着:如果您的所有方法都引用了您的所有属性,那么将其保存在一个大文件中被认为是良好的OOD。但通常情况下,现实世界中的问题并不是以这种单一的方式联系在一起的
如果你想去耦合一些东西,有一种说法,你应该去耦合那些彼此不交互的部分。在您的情况下,您可以(可能)将有关
attribA
的所有内容放在一个模块中,将有关attribB
的所有内容放在另一个模块中。因此,无论您使用的是什么具体的模块实现,它都不会变得混乱。好吧,至少在可读性方面,我采取的方法是将函数声明从返回块中拉出:
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
};
});
然后,如果这些函数中的代码是泛型/模块化的,则将该代码提取到实用程序文件中,并从那里调用它们。至少在可读性方面,我采取的方法是从返回块中提取函数声明:
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可能有几十种映射实现,一个简单的“map polyfill”谷歌搜索让我找到了这一种,它看起来很不错:我同意@Waog的观点,良好的OOD可能是解决您问题的方法。考虑到您正在为其编写setAttribA
函数的对象上可能有很多属性,为什么不使用映射稍微清理一下代码呢?JavaScript可能有几十种映射实现,一个简单的“map polyfill”谷歌搜索让我找到了这一种,它看起来很不错:似乎有机会使用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);
似乎有机会使用面向对象的方法来封装类似的功能。每个类都可以存在于自己的文件中。。。这似乎很好:
// 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
};
...
就我所知,在我看来,没有必要定义属性attribA
直到attribZ
,然后设置它们,然后再次为它们定义setter函数。您只需返回或访问screen.get('x')
where和when需要它
但如果出于某些原因,这是绝对必要的,那么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);
}
}
如果这个策略不能缓解这个问题,或者如果你认为我没有正确地理解问题,那么请尝试使情况更清楚,使我们更接近目标。
让我们考虑下面的代码摘录,我理解的问题在于:
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
};
...
就我所知,在我看来,没有必要定义属性attribA
直到attribZ
,然后设置它们,然后再次为它们定义setter函数。您只需返回或访问screen.get('x')
where和when需要它
但是如果绝对必要的话