我该如何优化这个switch语句?

4

我试图在我的应用程序中使用简单比较器来过滤一些数据,而不是传递函数作为例如传递给[].filter的筛选器。 比较器应该返回一个将成为筛选器的函数。

   var comparator = function( a, b, c ) { 
        switch( b ){
            case '>=': return function() { return this[a] >= c;}; break;
            case '<=': return function() { return this[a] <= c;}; break;
            case '<':  return function() { return this[a] < c;}; break;
            case '>':  return function() { return this[a] > c;}; break;
            case '=':  return function() { return this[a] == c;}; break;
            case '==': return function() { return this[a] === c;}; break;
            case '!=': return function() { return this[a] != c;}; break;
            default: return null;
        };

    }

假设我通过以下方式获得此函数:

  var filterFn = comparator.apply({}, /(.+)(=|>=|<=|<|>|!=|==|!==)(.+)/.exec( "id<4" ).slice(1) );


  someModel = someModel.objects.filter( filterFn );

目标是什么它会看起来像:
   someModel.get = function( filter ){ 
      return new Model(  
           this.objects.filter(
               comparator.apply({}, /(.+)(=|>=|<=|<|>|!=|==|!==)(.+)/.exec( "id<4" ).slice(1) 
           ) 
      );
   };
   var filtered = someModel.get( "id<4" );

问题是 - 我认为会有更多的运算符,但我不知道如何更简单地编写它。
使用Eval是不可能的。
这段代码既没有被执行也没有被测试,我只是写了出来以展示我的意思。

即使您将运算符“c”与已知的白名单可能值的数组进行比较,使用“eval()”是否也不可行? - Michael Berkowski
1
你需要一个完整的表达式解析器吗?例如不仅仅是单个运算符? - Lucero
2
请注意,在每个 return 语句后面不需要加上 break 语句。 - nnnnnn
@nnnnnn 我知道。 让我感到烦恼的是,我必须重写表达式中的运算符以及额外的开关列表。虽然可能无法以其他方式完成。 - abuduba
@Michael的提示值得深思... - abuduba
显示剩余2条评论
3个回答

5

将每个函数存储在一个对象中,可以是预定义的,也可以是动态创建的。

如果想要动态创建函数集,请按下面所示定义comparator对象。假设您没有扩展Object.prototype。如果扩展了,则必须在第一个循环内使用operators.hasOwnProperty(property)

// Run only once
var funcs = {};   // Optionally, remove `funcs` and swap `funcs` with `operators`
var operators = { // at the first loop.
    '>=': '>=',
    '<=': '<=',
    '<' :  '<',
    '>' :  '>',
    '=' : '==', //!!
    '==':'===', //!!
    '!=': '!='
}; // Operators

// Function constructor used only once, for construction
for (var operator in operators) {
    funcs[operator] = Function('a', 'c',
                       'return function() {return this[a] ' + operator + ' c};');
}

// Run later
var comparator = function(a, b, c) {
    return typeof funcs[b] === 'function' ? funcs[b](a, c) : null;
};

当调用comparator时,返回的函数如下所示:
function() {  return this[a] < c;   }// Where a, c are pre-determined.

这种方法可以这样实现(在JSFiddle上查看演示):
// Assumed that funcs has been defined
function implementComparator(set, key, operator, value) {
    var comparator, newset = [], i;

    if (typeof funcs[operator] === 'function') {
        comparator = funcs[operator](key, value);
    } else { //If the function does not exist...
        throw TypeError("Unrecognised operator");
    }

    // Walk through the whole set
    for (i = 0; i < set.length; i++) {
        //  Invoke the comparator, setting `this` to `set[i]`. If true, push item
        if (comparator.call(set[i])) {
            newset.push(set[i]);
        }
    }
    return newset;
}
var set = [ {meow: 5}, {meow: 3}, {meow: 4}, {meow: 0}, {meow: 9}]
implementComparator( set , 'meow', '<=', 5);
// equals: [ {meow: 5}, {meow: 3}, {meow: 4}, {meow: 0} ]

为了澄清,我构建了这个答案时牢记以下几点:

  • 原帖请求一个简单易于扩展的方法,并且该方法拥有未知或动态集合的操作符。
  • 代码基于原帖中的伪代码,没有更改任何可能影响原帖意图的内容。通过一些调整,该函数也可用于 Array.prototype.filter 或者 Array.prototype.sort
  • eval (或 Function) 不应该在每次调用 comparator 时使用。

1
@abuduba,你确定=====!=吗?你的代码没有实现否定的恒等运算符**!==。此外,如果你想实现一个稳定的计算器,请使用另一个RegExp。目前,你的模式将选择任何字符:(.+),如果存在,则也**会选择操作符。 - Rob W
1
@Rob W:Function(...)不就是伪装的eval吗? - KooiInc
@LightnessRacesinOrbit 如果它们被直接使用,那么是相同的。但请注意我如何使用Function构造函数。我创建了一个接受两个参数的函数。无论comparator执行多少次,Function(~= eval)的执行次数始终相同。您提出的eval建议必须每次运行,并且会导致内存泄漏,因为代码无法进行垃圾回收。 - Rob W
@RobW:我认为普遍的原因是它在本质上没有采用故障安全方法来保护一个本质上存在安全问题的功能。但是,这又怎样! - Lightness Races in Orbit
1
抱歉,但我认为OP没有意识到这首先是个不好的想法。因为这不是构建可重复使用的比较器的方法。至于内存泄漏问题,你有何证据?例如,在ES5严格模式下,eval代码的作用域链是空的 - PointedEars
显示剩余14条评论

2
不要这样动态地做...只创建一次函数会更有效率,而不是每次调用它们都创建一个新的函数,因为这样会导致内存泄漏。
var comparator = {
    ">=":  function(a, b) { return a >= b;},    
    "<=":  function(a, b) { return a <= b;},
    "add": function(a, b) { return a + b; },

compare: function(typeString, a, b){
    if(comparator.hasOwnProperty(typeString) == true){

        var theFunction = comparator[typeString];

        return theFunction(a, b);   
    }
    else{
        alert("typeString '" + typeString + "' not supported.");
     }
}, };

var test = comparator.compare(">=", 5, 4);

2
var comparator = (function () {
  var fns = {
    '>=': function (a, c) { return a >= c; },
    '<=': function (a, c) { return a <= c; },
    '<':  function (a, c) { return a < c; },
    '>':  function (a, c) { return a > c; },
    '=':  function (a, c) { return a == c; },
    '==': function (a, c) { return a === c; },
    '!=': function (a, c) { return a != c; }
  };

  return function (b) { 
    return fns.hasOwnProperty(b) ? fns[b] : null;
  };
}());

在这一点上,您可以看到没有什么比内联表达式更有效。我不清楚您为什么认为您需要事先如此动态。


好的,我看不出原始代码有多少意义(操作数和运算符都在闭包中 - 为什么不直接内联处理?),所以我发布了一个对我来说有意义的解决方案(固定运算符,可变操作数)。 - PointedEars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接