【问题标题】:How can I optimize this switch statement?如何优化此 switch 语句?
【发布时间】:2012-01-08 18:52:17
【问题描述】:

我尝试在我的应用程序中使用简单的比较器来过滤一些数据,并通过字符串过滤器代替功能,例如。传递给[].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() 是否也不可能?
  • 你是否需要一个完整的表达式解析器,例如不只有单一的运营商?
  • 请注意,您不需要在每个 return 之后使用 break 语句。
  • @nnnnnn 我知道。我必须重写表达式中的运算符以及附加的开关列表,这让我很恼火。虽然可能无法做到这一点。
  • @Michael 的提示令人深思……

标签: javascript optimization


【解决方案1】:

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

如果要动态创建函数集,请定义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.

这个方法可以这样实现(demo at 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} ]

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

  • OP 要求使用一组未知/动态运算符的简单、易于扩展的方法。
  • 代码基于 OP 的伪代码,没有更改任何可能影响 OP 意图的内容。通过一些调整,这个函数也可以用于Array.prototype.filterArray.prototype.sort
  • eval(或Function)不应在每次调用comparator时使用

【讨论】:

  • 我完全忘记了从字符串创建函数:) 谢谢。
  • @abuduba 你确定=====!= 吗?您的代码没有实现否定身份 !== 运算符。此外,如果您想实现一个可靠的计算器,请使用另一个 RegExp。目前,您的模式将选择 any 字符:(.+)also 是否选择运算符(如果存在)。
  • 这与使用eval基本相同。仅仅因为您不使用确切的函数名称并不意味着您没有从字符串创建代码然后运行它。 function not_eval(str) { return eval(str); } 也可以接受吗?
  • @Rob W: Function(...) eval 不是伪装的吗?
  • 对不起,但我认为 OP 并没有意识到这是一个坏主意。因为这不是你如何构建一个可重用比较器。至于内存泄漏问题,你的证据在哪里?例如,在 ES5 Strict Mode 下,eval 代码的作用域链是empty
【解决方案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);

【讨论】:

    【解决方案3】:
    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;
      };
    }());
    

    此时您可以看到没有什么比内联表达式更有效的了。我不清楚你为什么认为你需要事先变得如此有活力。

    【讨论】:

    • 好吧,我看不出原始代码有什么意义(操作数 运算符在一个闭包中——为什么不改为内联呢?),所以我发布了一个对我有意义的解决方案(固定运算符,可变操作数)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-30
    • 2019-07-03
    • 1970-01-01
    相关资源
    最近更新 更多