Я хотел бы выполнить функцию из верхней части стека. То есть любые переменные, определенные с помощью «var», должны быть определены в глобальном смысле. Это возможно? fn.call (окно) не работает.

<script>
  var foo = "I am top level foo.";

  var fn = function(){
    var foo = "Setting foo from in a function";
  }

  fn.call(window);  //doesn't work, var is still set locally in fn.
  alert(foo);  //desired effect will alert "setting foo from in a function"
</script>

Обратите внимание: я полностью осознаю, что отсутствие указания "var" делает элемент глобальной переменной. Проблема в том, что я не могу указать содержимое «fn», оно определяется пользователем, но ожидается, что оно будет запущено с верхнего уровня.

Я готов использовать eval, если это необходимо.

3
Sambo 25 Авг 2010 в 07:13

4 ответа

Лучший ответ

Я не уверен, что вы пытаетесь сделать, но вы не можете перезаписать значение foo таким образом. Когда вы указали var foo в глобальном контексте, он присоединяется к глобальному объекту (в глобальной области видимости).

Когда вы указали var foo в своей функции, вы объявили ее в рамках этой функции. Это означает, что он неизвестен внешнему миру + . Что вы могли бы сделать, это что-то вроде этого:

var fn = function() {
   this.foo = "Setting foo from in a function";
};

Или чтобы быть более явным:

var fn = function() {
  window.foo = "Setting foo from in a function";
};

Это то, что вы хотите?

+ Когда вы определили foo внутри функции, она была присоединена к объекту активации , а не к глобальному объекту . Каждый раз, когда вы вводите контекст выполнения для функции, создается новый объект активации . Поэтому каждый раз, когда вы вызываете функцию, вы получаете новый foo. Это также хорошо, иначе вы не сможете создавать локальные переменные для функции; они проникнут в глобальный контекст.

ОБНОВЛЕНИЕ

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

var foo = 2;

var fn = function() {
   var foo = 3;
}

console.log(foo); //prints 2

var fnSource = fn.toSource();
eval(fnSource.replace(/^\(function\s*\(\)\s*{/, "").replace(/}\s*\)$/, ""));

console.log(foo); //prints 3

Это исключит (function () { и }), так что в итоге вы получите только код посередине, который затем пройдете через eval.

Еще раз, я должен подчеркнуть, что я не рекомендую делать это.

2
Vivin Paliath 25 Авг 2010 в 03:33

Вместо использования var установите переменные внутри объекта this. Таким образом, если вы вызовете функцию с window, вы будете устанавливать «глобальные» переменные:

window.foo = "I am top level foo.";

var fn = function(){
    this.foo = "Setting foo from in a function";
}

fn.call(window);
alert(foo);

Затем вы также можете вызвать функцию с другим контекстом:

var anObject = { foo: "Initial Value" };
fn.call(anObject);
alert(anObject.foo); // Will be the modified value
1
Jacob 25 Авг 2010 в 03:23

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

Лучше всего, чтобы пользователь изменил свою функцию.

Полагаю, что если бы не было этого, вы могли бы перепрыгнуть через огненные кольца и выполнить поиск и замену строк в исходном коде функции toSource() и удалить ключевые слова var. Или возьмите toSource(), удалите обертки function { ... }, а затем eval() этот код. Это может работать. Но это чрезмерное количество обтекаемых, очень хрупких и серьезных запахов кода.


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

var fn = function() {
    foo = "Setting foo from in a function";
}

Если вы хотите явно повлиять на глобальную область действия, измените объект window, который является контейнером для глобальных объектов:

var fn = function() {
    window.foo = "Setting foo from in a function";
}
1
John Kugelman 25 Авг 2010 в 03:32

Вы можете получить этот эффект, не меняя fn, но этот код не для слабонервных:

<script type="text/javascript">
var gFoo = "";

var fn = function() {
    var foo = "Setting foo from in a function";
}

var fnText = fn.toString();
var newFunction = fnText.substring(0, fnText.length - 1) + "gFoo = foo; }";

eval("var x = " + newFunction + "; x();");
alert(gFoo);
</script>
0
Ian Henry 25 Авг 2010 в 03:34