Подтвердить что ты не робот

Как определить, является ли функция пустой

Я хочу, чтобы проверить, является ли данная функция пустой или нет. То есть в теле нет ничего, например:

function foo() {}
function iAmEmpty(a) {
    // yep, empty
}

С некоторыми начальными играми, у меня есть что-то, что, я думаю, может быть в порядке, используя toString() и некоторые регулярные выражения.

function foo(a, b, c) {}

/^function[^{]+\{\s*\}/m.test(foo.toString());  // true

function bar(a, b, c) { var d; }

/^function[^{]+\{\s*\}/m.test(bar.toString());  // false

Мне просто интересно, есть ли лучший подход? Есть ли проблемы с вышесказанным, которые вы видите?

4b9b3361

Ответ 1

Это не рекомендуется. Не существует стандартного определения того, что должен возвращать метод toString(), поэтому, даже если вы будете работать в текущих браузерах, будущие браузеры могут по праву изменить их реализацию и сломать ваш код.

Kangax кратко написал об этом: http://perfectionkills.com/those-tricky-functions/

Ответ 2

Функции стрелок...

Как я уверен, вы знаете, javascript поддерживает функции стрелок, которые действительно кратки, но, к сожалению, не работают с вашим аккуратным regex.

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

isEmpty = f => /^function[^{]+\{\s*\}/m.test(f.toString())

Теперь мы можем легко проверить пустую функцию:

function eF() {}

который, как и ожидалось, с помощью isEmpty(eF) возвращает true.

И еще раз с фактической функцией:

function retOne() {return 1;}

который, как и ожидалось, с isEmpty(retOne) возвращает false.

Однако проблема, с которой я столкнулась, связана со стрелочными функциями, чтобы снова инициализировать пустой, мы имеем более короткий синтаксис оригинала:

eF = () => {}

и версия 'stringified', которая отличается от предыдущей:

"() => {}"

поэтому, конечно, в этом случае вызов isEmpty(eF) возвращает false, когда мы хотим true. Я не уверен, что вам нужно проверить, все ли функции (т.е. Включая arrow functions) пусты, но если вы это сделаете, ваш regex потребуется изменить...

Я не очень люблю писать их сам, но попытался сделать пару, и еще одна вещь, которую вы можете рассмотреть, - это мягкий характер arrow functions, особенно этой части документация:

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: (param1, param2, …, paramN) => { return expression; }

// Parentheses are optional when there only one parameter name:
(singleParam) => { statements }
singleParam => { statements }

который показывает, как не всегда нужны фигурные скобки {...}. Итак, эта функция:

retOne = () => 1

есть valid и может затруднить формирование нового regex. Один из обходных решений, о котором я думал, состоит в том, чтобы просто удалить все фигурные скобки из f.toString(), используя:

str.replace(/[{}]/g, '').

а затем откройте там regex test.

Надеюсь, это будет полезно рассмотреть, хотите ли вы, чтобы arrow functions также мог быть протестирован.

Ответ 3

Лучшее, что вы можете попробовать, чтобы максимально использовать возможности (так как это довольно сложно достичь), заключается в добавлении acorn или esprima (работает со стрелочными функциями) и обрабатывать функцию JavaScript. Он будет подделывать его для вас, чтобы вы могли разобрать его, чтобы вы могли обработать его по своему усмотрению, проверяя, действительно ли там нулевой код внутри, или есть только объявления переменных без каких-либо вычислений или возврата и т.д.

Достаточно просто реализовать:

function check(f) {
  console.log("");
  console.log("Checking", f);
  var syntax = esprima.parse(f);
  if (syntax.body.length != 1) {
    console.log("Weird process");
  } else {
    function processBody(body) {
      if (!body || body.length == 0) {
        console.log("Body seems empty. YAY!");
      } else {
        for (var i = 0, command; command = body[i]; i++) {
          if (command.type != "VariableDeclaration") {
            console.log("Body has a", command.type, ", so is not empty.");
            return;
          }
        }
        console.log("Body has only variable declarations, so is empty! (or not, whatever fit your needs)");
      }
    }
    function process(dec) {
      if (dec.type != "FunctionDeclaration") {
        console.log("Weird declaration");
      } else {
        console.log("Function", dec.id.name, "has", dec.params.length, "params");
        processBody(dec.body.body);
      }
    }
    process(syntax.body[0]);
  }
}

check("function hasReturn(arg1, arg2) {var a = arg1 + arg2;return a;}");
check("function hasArgumentsButNoBody(arg1, arg2) {}");
check("function isCompletelyEmptyWithSpacesAndTabsAndLinefeeds() {   \t    \t   \r\n \r\n  }");
check("function hasOnlyVariables() {var a, b = 2; var c = 1 + b;}");
<script src="https://cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.min.js"></script>

Ответ 4

Я не вижу использования для этого, но вы можете сделать это проще, привязывая шаблон к концу строки.

  /[^{\s]\s*\}$/.test(String(bar))

Ответ 5

Function.prototype.hasBody = function() {
    return !/{\s*}$/.test(this.toString());
};

Ответ 6

Прост просто проверить, содержит ли функция, а затем проверять содержимое, если оно пустое или нет.
проверьте Plunker
здесь полный рабочий код:

function foo(a, b, c) {}

function bar(a, b, c) {
  var d;
}

function isEmpty(value) {
  return (value == null || value.length === 0);
}

function check(test) {
  var entire = test.toString();
  var body = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}"));
  return body;

}
console.log(isEmpty(check(foo)), isEmpty(check(bar))); //return true false