Разрешение внешних ссылок в функции javascript для сериализации

var foo = (function(){
  var x = "bar";
  return function(){
    console.log(x);
  };
})();

console.log(foo.toString()); // function() {console.log(x);}
(foo)(); // 'bar'
eval('(' + foo.toString()+')()')); // error: x is undefined

Существует ли способ разрешения (изменения) функции, чтобы ссылки из внешней области действия становились локальными ссылками, например:

function() {console.log(x);}

становится:

function() {console.log("bar");}

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

Может быть, можно разобрать функцию на абстрактное синтаксическое дерево, а затем изменить ее? Ссылка всегда будет вне области действия (недоступна), верно?

Цель:

Я сериализую функцию фильтра из среды выполнения узла в среду выполнения postgresql plv8. Прямо сейчас функция фильтра имеет интерфейс: dbClient.filter((row, age) => row.age > age), ageFromOuterScope).then(matches => ...)

Мне нужен интерфейс dbClient.filter((row) => row.age > age)).then(matches => ...), где age является ссылкой из внешней области.

Обновление:

Я могу представить только одно решение. Проанализируйте функцию, найдите ссылки на переменные вне функции, а затем перепишите исходную функцию:

function(row) {
   return row.age > age
}

To:

function(row, age) {
  return row.age > age
}

Обнаруженные переменные также должны быть добавлены в строку, представляющую массив, например:

var arrayString = '[age]'

И затем оцените строку:

var functionArgs = eval(arrayString)

И наконец:

dbClient.filter(modifiedFunction, ...functionArgs).then(matches => ...)

person Jacob    schedule 11.10.2015    source источник
comment
В большинстве случаев такие переменные изменяются во время выполнения, поэтому такие преобразования изменят поведение кода.   -  person Alexander O'Mara    schedule 11.10.2015
comment
Два комментария: во-первых, это не по теме переполнения стека, поскольку оно запрашивает библиотеку. Во-вторых, это не то, как область видимости работает в Javascript, и не должно быть так, поэтому она не работает для вас. Поскольку это не работает таким образом, как метод может правильно предсказать, как принять это решение? Это, в свою очередь, делает вопрос слишком широким для переполнения стека.   -  person David L    schedule 11.10.2015
comment
Я изменил вопрос. Как насчет использования АСТ?   -  person Jacob    schedule 11.10.2015
comment
Конечно, это может сработать. Если бы вы попробовали подход AST и столкнулись с трудностями, это сделало бы вопрос актуальным для SO. Поскольку это все еще стоит, это не по теме.   -  person David L    schedule 11.10.2015
comment
Теперь я думаю, что вопрос в порядке   -  person Jacob    schedule 11.10.2015
comment
Я думаю, что ответ: Нет, такой техники не существует.   -  person jfriend00    schedule 11.10.2015
comment
С абстрактным синтаксическим деревом вы получите значение переменной внешней области во время синтаксического анализа. Значения времени выполнения - другое дело. Я согласен, это невозможно. Сериализация/экспорт функций, зависящих от переменных внешней области видимости, — бесполезное занятие. Я чувствую запах XY-проблемы. Чего вы пытаетесь достичь?   -  person Tomalak    schedule 11.10.2015
comment
Я сериализую функцию фильтра из среды выполнения узла в среду выполнения postgresql plv8. Прямо сейчас функция фильтра имеет интерфейс: dbClient.filter((row, age) => row.age › age), ageFromOuterScope).then(matches =› ...). Мне нужен интерфейс dbClient.filter((row) =› row.age › age)).then(matches =› ...), где age — это ссылка из внешней области видимости.   -  person Jacob    schedule 11.10.2015
comment
Связано: stackoverflow.com/questions/4670805/   -  person nha    schedule 16.10.2015
comment
Спасибо @nha. Я не уверен, как это связано. Можно поподробнее?   -  person Jacob    schedule 16.10.2015


Ответы (3)


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

var foo = (function(){
  var x = "bar";

  var f = function(){
    console.log(x);
  };

  f.decl = function() {
      return f.toString().replace("(x)", "(\""+x+"\")");
  }

  return f;

})();

console.log(foo.decl()); // function() {console.log("bar");}

eval("("+foo.decl()+")()"); // bar
person Fluster    schedule 23.10.2015

Я прогнал foo вашего главного кодового ящика через Google Closure Compiler, и он дал мне следующее:

var foo=function(){return function(){console.log("bar")}}();foo;

не ТОЧНО то, что вы хотите, но вы можете получить то, что хотите, используя eval() и/или toString(), как вы уже возились.

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

person dandavis    schedule 17.10.2015

Вы можете привязать x к самому функциональному объекту.

var foo = (function(){
  var x = "bar";
  return function(){
    this.x = x;
    console.log(this.x);
  };
})();

(foo)() // 'bar'

console.log(foo.toString()); // 'function() { this.x = x; console.log(this.x) }'

eval('(' + foo.toString()+')()'); // 'bar' 

person Downhillski    schedule 17.10.2015
comment
Когда вы делаете (foo)(), x будет присоединен к объекту window из-за this.x = x. Вот почему он все еще работает, когда вы вызываете eval с foo.toString(), потому что x уже имеет предустановленное значение бара. Если вы попытаетесь запустить eval в другой глобальной области, произойдет сбой. - person ingaham; 21.08.2018