как избежать вложенного переключателя? - PullRequest
4 голосов
/ 24 марта 2009

Я создаю контекстное меню на JavaScript для веб-приложения. Меню может появляться в разных контекстах и ​​иметь разные варианты. Я мог бы иметь разные функции для каждого контекста / выбора:

grid1_delete()
grid1_duplicate()
grid2_delete()
grid2_add()
grid2_duplicate()

и жесткий код тех, кто находится в процессе создания меню. Что мне не нравится в этом, так это то, что, вероятно, будет много повторяющегося кода. Итак, я думал об использовании функции диспетчера, но это приводит к потенциально длинному вложенному оператору переключения, таким образом:

function contextMenuClick(context, menuItem) {
    var action = menuItem.innerHTML;
    switch (context) {
        case 'grid1':
            switch(action) {
                 case('delete'):
                      // do delete for grid1
                      break;
                 case('duplicate'):
                      // do duplicate for grid1
                      break;
                 default:
                      console.log('undefined action in contextMenuClick/grid1: ' + context);
            }
            break;
        case 'grid2':
            switch(action) {
                 case('add'):
                      // do add for grid2
                      break;
                 case('delete'):
                      // do delete for grid2
                      break;
                 case('duplicate'):
                      // do duplicate for grid2
                      break;
                 default:
                      console.log('undefined action in contextMenuClick/grid2: ' + context);
            }
            break;
        default:
            console.log('undefined context in contextMenuClick: ' + context);
        }

Тьфу. Должен быть лучший способ. Может быть, диспетчер больше проблем, чем стоит. Я рассмотрел некоторые из связанных сообщений , но я не совсем понимаю, как применить их к этой конкретной ситуации.

Ответы [ 4 ]

7 голосов
/ 24 марта 2009

Операторы Switch очень редко необходимы в Javascript. В общем, вы можете просто использовать такие объекты, как словари / карты и выполнять поиск напрямую: foo.bar эквивалентно foo['bar'].

Кроме того, для «глобальных» переменных some_global_func() эквивалентно window.some_global_func(), что также может быть записано как var f = 'some_global_func'; window[f](): вам никогда не нужно eval для выбора переменная или вызов функции динамически на основе ее имени. В общем, при этом, однако, вы предпочитаете хранить функцию в объекте, а не в глобальной области видимости (то есть в объекте window).

Итак, предполагая, что grid1_delete и grid2_delete принципиально различны и не могут быть объединены в универсальную функцию, вы могли бы сделать что-то вроде следующего, не сильно меняя код:

var grid_actions = {
    'grid1': {
        'delete': function() { /* ... */ },
        'duplicate': function() { /* ... */ }
    },
    'grid2': {
        'delete': function() { /* ... */ },
        'add': function() { /* ... */ },
        'duplicate': function() { /* ... */ }
    }
}

function contextMenuClick(context, menuItem) {
    var action = menuItem.innerHtml;
    if (context in grid_actions) {
        if (action in grid_actions[context]) {
            grid_actions[context][action]();
        } else {
            console.log('undefined action in contextMenuClick/' + context + ': ' + action);
        }
    } else {
        console.log('undefined context in contextMenuClick: ' + context);
    }
}

Лучшее решение, однако, состоит в том, чтобы реорганизовать вещи, чтобы эти функции были методами объектов для каждого контекста, как предлагает @ le dorfier .

2 голосов
/ 24 марта 2009

Как насчет передачи фактической ссылки на объект для "context" вместо просто строки? Таким образом, у вас есть только один оператор switch:

function contextMenuClick(grid, menuItem) {
    var action = menuItem.innerHTML;
    switch(action) {
       case('delete'):
          grid.delete();
          break;
       case('duplicate'):
          grid.duplicate();
          break;
    }
}

Еще лучше, просто связать обработчик напрямую с правильным объектом / методом.

1 голос
/ 24 марта 2009

Самое простое и удобное упрощение состоит в том, чтобы каждый переключатель имел функцию, содержащую переключатель.

Если у вас есть объект для каждого из ваших контекстов, вы можете добавить функцию к каждому объекту, которая принимает меню в качестве аргумента.

0 голосов
/ 24 марта 2009

Что-то вроде использования eval (context + "_" + action + "();"), вероятно, сработает, но это довольно опасно, так как это позволит вашему клиенту выполнять почти произвольную функцию в вашем скрипте. (все, что соответствует X_Y). Да. Эвал в основном злой.

Как насчет

switch(context+"_"+action) { 
    case ("grid1_add"):  grid1_add(); 
[...]
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...