在 DOM 上以“向上”和“向下”方向操作时如何避免重复代码
How to avoid duplicate code when operating on DOM in "up" and "down" direction?
我正在编写一个JS网络应用程序客户端。用户可以编辑文本项的列表/树(例如,待办事项列表或注释(。我经常用jQuery操作DOM。
用户可以使用键盘上下导航列表(类似于GMail中的J/K键(,并执行其他一些操作。其中许多操作具有镜像"向上"/"向下"功能,例如
$.fn.moveItemUp = function() {
var prev = this.getPreviousItem();
prev && this.insertBefore(prev);
// there's a bit more code in here, but the idea is pretty simple,
// i.e. move the item up if there's a previous item in the list
}
$.fn.moveItemDown = function() {
var next = this.getNextItem();
next && this.insertAfter(next);
// ....
}
现在,在我的代码中的多个位置重复了具有两个几乎相同函数的模式,因为项目列表/树上的许多操作几乎是对称的。
问:如何优雅地重构以避免代码重复?
到目前为止,我想出的简单方法是使用 .apply((...
$.fn.moveItem = function(direction) {
var up = direction === 'up',
sibling = up ? this.getPreviousItem() : this.getNextItem(),
func = up ? $.fn.insertBefore : $.fn.insertAfter;
// ...
if (! sibling) { return false; }
func.apply(this, [ sibling ]);
// ...
};
好处是当代码的其他元素的结构需要更改 moveUp/moveDown 时,维护更容易。我已经需要多次稍微更改代码,并且我始终需要记住,我需要在两个地方进行更改......
但我对"统一"版本不满意,因为:
- 实现比简单的 moveUp/moveDown 更复杂,因此将来可能不那么容易维护。我喜欢简单的代码。所有这些参数,三元运算,每个应该"向上"和"向下"的函数中的.apply((技巧......
- 不确定显式引用 jQuery.fn.* 函数是否安全(毕竟它们应该通过将它们应用于 jQuery 对象 $('...'(来使用。
在使用 DOM 或类似结构时,您如何解决那些"几乎相同的代码"情况?
您可以做的一件事是使用闭包来隐藏从用户传递的配置参数:
var mk_mover = function(direction){
return function(){
//stuff
};
};
var move_up = mk_mover("up");
var move_down = mk_mover("down");
如果你想继续朝这个方向前进,基本上就是决定将参数传递给公共实现的最佳方式是什么,并且没有一个单一的最佳解决方案。
一个可能的方向是使用OO风格的方法,将配置"策略"对象传递给实现函数。
var mk_mover = function(args){
var get_item = args.getItem,
ins_next = args.insNext;
return function(){
var next = get_item.call(this);
next && ins_next.call(this, next);
};
};
var move_up = mk_mover({
get_item : function(){ return this.getPrevious(); },
get_next : function(x){ return this.insertBefore(x); }
});
var move_down = mk_mover({/**/});
当策略界面(不同方向的方法(较小且相对恒定时,以及当您希望开辟将来添加新方向的可能性时,我更喜欢这样做。
当我知道方向集和方法类型都不需要太大变化时,我也倾向于使用它,因为 JS 对 OO 的支持比对 switch 语句的支持更好。
另一个可能的方向是您使用的枚举方法:
var mk_mover = function(direction){
return function(){
var next = (
direction === 'up' ? this.getNextItem() :
direction === 'down' ? this.getPreviousItem() :
null );
if(next){
if(direction === 'up'){
this.insertAfter(next);
}else if(direction === 'down'){
this.insertBefore(next);
}
}
//...
};
}
有时你可以使用整洁的对象或数组而不是switch语句来使事情变得更漂亮:
var something = ({
up : 17,
down: 42
}[direction]);
虽然我不得不承认这有点笨拙,但它的优点是,如果你的方向集在早期是固定的,你现在有很大的灵活性,可以在需要时以自包含的方式添加新的 if-else 或 switch 语句(无需在其他地方向策略对象添加一些方法......
顺便说一句,我认为您建议的方法位于我建议的两个版本之间的不舒服中间地带。
如果您所做的只是根据传递的值方向标签在函数内部进行切换,那么最好直接在您需要的位置进行切换,而不必将内容重构为单独的函数,然后必须做很多烦人的 .call 和 .apply 的东西。
另一方面,如果您经历了定义和分离函数的麻烦,您可以做到这一点,以便您直接从调用者(以策略对象的形式(接收它们,而不是自己手动调度。
我以前遇到过类似的问题,而且我发现没有一个很好的模式来处理反向执行看似相似的任务。
虽然作为人类,它们相似是有道理的,但就编译器而言,您实际上只是调用任意方法。唯一明显的重复是事物的调用顺序:
- 查找我们想在之前或之后插入的项目
- 检查项目是否存在
- 在当前项目之前或之后插入它
您已经以一种基本上执行这些检查的方式解决了问题。但是,您的解决方案有点不可读。以下是我如何解决这个问题:
$.fn.moveItem = function(dir){
var index = dir == 'up' ? 0:1,
item = this['getPreviousItem','getNextItem'][index]();
if(item.length){
this['insertBefore','insertAfter'][index].call(this,item);
}
}
通过使用数组索引,我们可以看到调用时调用的内容比您的示例中容易一些。此外,使用 apply 是不必要的,因为您知道要传递多少参数,因此调用更适合该用例。
不确定这是否更好,但它更短(返回错误是必要的吗?(并且更具可读性 IMO。
在我看来,集中操作比可读性稍微重要一些——如果你想优化、更改或附加到移动某些东西的动作,你只需要在一个地方完成。您可以随时添加注释来解决可读性问题。
我认为单函数方法是最好的,但我会将其与向上/向下的东西分开。创建一个函数将项目移动到索引或另一个项目之前(我更喜欢后者(,并在那里处理所有"移动"事件,而不依赖于方向。
function moveBefore(node) {
this.parentNode.insertBefore(this, node); // node may be null, then it equals append()
// do everything else you need to handle when moving items
}
function moveUp() {
var prev = this.previousSibling;
return !!prev && moveBefore.call(this, prev);
}
function moveDown() {
var next = this.nextSibling;
return !!next && moveBefore.call(this, next.nextSibling);
}
- 如何将函数包装在函数中以避免代码重复
- 如何避免“;使用数组文字表示法“;以下javascript代码中的jslint错误
- 如何在jquery中为object键创建一个工作变量以避免额外的代码
- 如何避免代码重复
- 避免jQuery中的重复代码
- 如何重写此 Javascript 代码以避免 JSHint 警告
- 如何避免重复代码(加载页面时切换)
- 如何避免重复以下替换代码
- 在分配事件时避免代码重复
- OOP ajax 以避免代码重复
- jQuery/can'我找不到避免代码添加类的方法
- Meteor模板-继承或外包事件以避免代码冗余
- ExtJS - 如何创建可重用的函数以避免代码冗余
- 在配置 JavaScript 对象文字构建的小部件时,如何避免代码重复
- JavaScript:getElementById-避免代码重复
- 我可以在ember中编写helper以避免代码重复
- 克隆使用不同选择器找到的元素,避免代码重复
- 附加HTML块“;更多的次数”;通过避免代码重复
- 如何避免代码重复在JavaScript中附加的片段
- JQuery.美元.Post request .done() .fail()避免代码重复