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

Новая функция() с переменными параметрами

Мне нужно создать функцию с переменным числом параметров с помощью конструктора new Function(). Что-то вроде этого:

args = ['a', 'b'];
body = 'return(a + b);';

myFunc = new Function(args, body);

Можно ли сделать это без eval()?


Большое спасибо, ребята! На самом деле, a + b не был моей главной задачей. Я работаю над кодом, который будет обрабатывать и расширять шаблоны, и мне нужно было передать неизвестное (и переменное) количество аргументов в функцию, чтобы они были введены как локальные переменные.

Например, если шаблон содержит:

<span> =a </span> 

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

var expand = tplCompile('template', a, b, c) 

а затем вызывает

expand(4, 2, 1) 

Мне нужно заменить =a на 4. И да, я хорошо знаю, чем функция похожа на eval() и работает очень медленно, но у меня нет другого выбора.

4b9b3361

Ответ 1

Вы можете сделать это, используя apply():

args = ['a', 'b', 'return(a + b);'];
myFunc = Function.apply(null, args);

Без оператора new Function дает точно такой же результат. Вы можете использовать функции массива, такие как push(), unshift() или splice(), чтобы изменить массив, прежде чем передавать его, чтобы применить.

Вы также можете просто передать строку аргументов, разделенных запятыми, в Function:

args = 'a, b';
body = 'return(a + b);';

myFunc = new Function(args, body);

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

myFunc = function () {
    var total = 0;

    for (var i=0; i < arguments.length; i++)
        total += arguments[i];

    return total;
}

myFunc(a, b);

Это будет более эффективно, чем использование конструктора Function, и, вероятно, является гораздо более подходящим способом достижения того, что вам нужно.

Ответ 2

@Ответ AndyE правильный , если конструктору все равно, используете ли вы ключевое слово new или нет. Некоторые функции не так просты.

Если вы обнаружите, что вам нужно использовать ключевое слово new, и вам нужно отправить переменное количество аргументов функции, вы можете использовать этот

function Foo() {
  this.numbers = [].slice.apply(arguments);
};


var args = [1,2,3,4,5]; // however many you want
var f = Object.create(Foo.prototype);
Foo.apply(f, args);

f.numbers;          // [1,2,3,4,5]
f instanceof Foo;   // true
f.constructor.name; // "Foo"

ES6 и выше!

// yup, that easy
function Foo (...numbers) {
  this.numbers = numbers
}

// use Reflect.construct to call Foo constructor
const f =
  Reflect.construct (Foo, [1, 2, 3, 4, 5])

// everything else works
console.log (f.numbers)          // [1,2,3,4,5]
console.log (f instanceof Foo)   // true
console.log (f.constructor.name) // "Foo"

Ответ 3

Если вам просто нужна функция sum(...):

function sum(list) {
    var total = 0, nums;
    if (arguments.length === 1 && list instanceof Array) {
        nums = list;
    } else {
        nums = arguments;
    }
    for (var i=0; i < nums.length; i++) {
        total += nums[i];
    }
    return total;
}

Затем

sum() === 0;
sum(1) === 1;
sum([1, 2]) === 3;
sum(1, 2, 3) === 6;
sum([-17, 93, 2, -841]) === -763;

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

Ответ 4

new Function(...)

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

Давайте рассмотрим его с JSLitmus и запустите небольшой тест script:

<script src="JSLitmus.js"></script>
<script>

JSLitmus.test("new Function ... ", function() { 
    return new Function("for(var i=0; i<100; i++) {}"); 
});

JSLitmus.test("function() ...", function() { 
       return (function() { for(var i=0; i<100; i++) {}  });
});

</script>

Что я сделал выше, создайте теги function expression и function constructor, выполняя ту же операцию. Результат следующий:

Результат производительности FireFox

FireFox Performance Result

Результат работы IE

IE Performance Result

Исходя из фактов, я рекомендую использовать function expression вместо function constructor

var a = function() {
 var result = 0;
 for(var index=0; index < arguments.length; index++) {
  result += arguments[index];
 }
 return result;
 }
alert(a(1,3));

Ответ 5

Здесь вы можете написать несколько разных способов.

// assign normally
var ab = ['a','b'].join('');
alert(ab);
// assign with anonymous self-evaluating function
var cd = (function(c) {return c.join("");})(['c','d']);
alert(cd);
// assign with function declaration
function efFunc(c){return c.join("");}
var efArray = ['e','f'];
var ef = efFunc(efArray);
alert(ef);
// assign with function by name
var doFunc = function(a,b) {return window[b](a);}
var ghArray = ['g','h'];
var ghFunc = function(c){return c.join("");}
var gh = doFunc(ghArray,'ghFunc');
alert(gh);
// assign with Class and lookup table
var Function_ = function(a,b) {
  this.val = '';
  this.body = b.substr(0,b.indexOf('('));
  this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1);
  switch (this.body) {
    case "return": 
      switch (this.args) {
        case "a + b": this.val = a.join(''); break;
      }
    break;
  }
} 
var args = ['i', 'j'];
var body = 'return(a + b);';
var ij = new Function_(args, body);
alert(ij.val);

Ответ 7

function construct(){
         this.subFunction=function(a,b){
         ...  
         }
}
var globalVar=new construct();   

против.

var globalVar=new function (){
              this.subFunction=function(a,b){
              ...
              }
}

Я предпочитаю вторую версию, если есть вспомогательные функции.

Ответ 8

В этом примере я использовал lodash:

function _evalExp(exp, scope) {
  const k = [null].concat(_.keys(scope));
  k.push('return '+exp);
  const args = _.map(_.keys(scope), function(a) {return scope[a];});
  const func = new (Function.prototype.bind.apply(Function, k));
  return func.apply(func, args);
}

_evalExp('a+b+c', {a:10, b:20, c:30});

Ответ 9

b.apply(null, arguments) не работает должным образом, когда b наследует прототип, потому что 'new' опущен, базовый конструктор не вызывается.