Возможно ли достичь динамического определения объема в JavaScript, не прибегая к eval?

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

<code>x=1
function g () { echo $x ; x=2 ; }
function f () { local x=3 ; g ; }
f # does this print 1, or 3?
echo $x # does this print 1, or 2?
</code>

Вышеприведенная программа печатает 1 и затем 2 на языке с лексической областью, и она печатает 3 и затем 1 на языке с динамической областью. Так как JavaScript имеет лексическую область видимости, он напечатает 1, а затем 2, как показано ниже:

<code>var print = x => console.log(x);

var x = 1;

function g() {
    print(x);
    x = 2;
}

function f() {
    var x = 3;
    g();
}

f();           // prints 1

print(x);      // prints 2</code>

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

<code>var print = x => console.log(x);

var x = 1;

function g() {
    print(x);
    x = 2;
}

function f() {
    // create a new local copy of `g` bound to the current scope
    // explicitly assign it to a variable since functions can be unnamed
    // place this code in the beginning of the function - manual hoisting
    var g_ = eval("(" + String(g) + ")");
    var x = 3;
    g_();
}

f();                         // prints 3

print(x);                    // prints 1</code>

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

Edit: Это то, что я пытаюсь реализовать без использованияeval:

<code>var print = x => console.log(x);

function Class(clazz) {
    return function () {
        var constructor;
        var Constructor = eval("(" + String(clazz) + ")");
        Constructor.apply(this, arguments);
        constructor.apply(this, arguments);
    };
}

var Rectangle = new Class(function () {
    var width, height;

    constructor = function (w, h) {
        width = w;
        height = h;
    };

    this.area = function () {
        return width * height;
    };
});

var rectangle = new Rectangle(2, 3);
print(rectangle.area());</code>

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

 Ken Russell11 мар. 2013 г., 08:29
Будет ли работать приведенный ниже код для вас? var x = 1; function g () {print (this.x); this.x = 2; } функция f () {var x = 3; this.g (); } печать (х);
 Aadit M Shah08 апр. 2012 г., 09:28
@ChrisWesseling - я обновил свой вопрос, чтобы показать, что заставило меня опубликовать его. Вышеуказанная программа отлично работает и работает на всех платформах. Я считаю, что у него есть большой потенциал для создания шаблонов классов и много других вещей. Это зависит от широкой публики, чтобы использовать это с умом. Возможно, одна из единственных веских причин для использованияeval.
 Chris Wesseling08 апр. 2012 г., 08:56
Красиво написано, интересный вопрос. Хотя он требует объективного ответа, если кто-то не покажет, как это можно сделать, я подозреваю, что это вызовет много споров и субъективных ответов о том, почему это нельзя или нельзя делать. Что привело вас к этому вопросу?
 user12344455562108 апр. 2012 г., 11:19
похожий вопрос:stackoverflow.com/questions/10031399
 Aadit M Shah08 апр. 2012 г., 09:54
Причина, по которой я хочу использовать динамическую область видимости, заключается в том, что я могу вводить частные переменные в область действия функции. Например, в моей вышеупомянутой программе я также могу передать переменнуюuber это указывает на родителя данного класса. Эта переменная должна быть доступна для области видимости, но она не должна быть доступной для общественности. Следовательно, я не могу просто установить его на экземпляр класса и назвать его днем. Таким образом, обходной обходной путь.

Ответы на вопрос(7)

Я знаю, что это не совсем точный ответ на вопрос, но это слишком много кода, чтобы вставить его в комментарий.

В качестве альтернативного подхода вы можете захотеть взглянуть на ExtJSextend функция. Вот как это работает:

var Rectangle = Ext.extend(Object, {
    constructor: function (w, h) {
        var width = w, height = h;
        this.area = function () {
            return width * height;
        };
    }
});

С открытыми свойствами вместо закрытых переменных:

var Rectangle = Ext.extend(Object, {
    width: 0,
    height: 0,  

    constructor: function (w, h) {
        this.width = w;
        this.height = h;
    },

    area: function () {
        return this.width * this.height;
    }
});

В вашем случае, вместо того, чтобы пытаться использовать динамическую область видимости для установки конструктора, что если вы использовали возвращаемое значение?

function Class(clazz) {
    return function () {
        clazz.apply(this, arguments).apply(this, arguments);
    };
}

var Rectangle = new Class(function () {
    var width, height;

    this.area = function () {
        return width * height;
    };

    // Constructor
    return function (w, h) {
        width = w;
        height = h;
    };
});

var rectangle = new Rectangle(2, 3);
console.log(rectangle.area());
 Aadit M Shah08 апр. 2012 г., 10:00
Я мог бы сделать это, но, как я объяснил в своем последнем комментарии к моему вопросу - использование динамического определения объема позволяет мне вводить в область действия функции столько переменных, сколько я хочу. Вы можете вернуть только одно значение.

Вы можете смоделировать динамическую область видимости, используя глобальные переменные, если у вас есть способ сделать синтаксический сахар (например, макросы с gensyms) и если у вас есть защита от перемотки.

Макрос может появиться для повторного связывания динамической переменной, сохранив ее значение в скрытой лексической структуре и затем присвоив ей новое значение. Код unwind-protect гарантирует, что независимо от того, как завершится этот блок, будет восстановлено исходное значение глобальной переменной.

Псевдокод Lisp:

(let ((#:hidden-local dynamic-var))
  (unwind-protect
    (progn (setf dynamic-var new-value)
           body of code ...)
    (set dynamic-var #:hidden-local)))

Конечно, это не потокобезопасный способ создания динамической области, но если вы не выполняете многопоточность, это подойдет! Мы бы спрятали его за макрос, как:

(dlet ((dynamic-var new-value))
   body of code ...)

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

 Aadit M Shah08 апр. 2012 г., 08:52
Это на самом деле не вариант. Макропроцессоры работают во время компиляции. Что мне нужно, это решение во время выполнения.
 08 апр. 2012 г., 08:38
Я обновил комментарий с помощью иллюстрации кода с использованием Lisp; надеюсь, это поможет.
 08 апр. 2012 г., 08:49
Похоже, защита от перемотки JavaScripttry { ... } finally { ... }, Таким образом, вы бы установить новое значение вtry блок, а затем восстановить переменную до ее сохраненного значения вfinally, В JavaScript нет макросов, но можно использовать препроцессор типа «текст в текст». В любом случае JavaScript-кодеры используют такие вещи, как Closure & compot & quot; это сокращает код JavaScript.
 Aadit M Shah08 апр. 2012 г., 08:36
Не могли бы вы разработать более простые термины? Возможно, связать меня с интернет-ресурсом?
 Aadit M Shah08 апр. 2012 г., 08:40
Думаю, мне лучше начать изучать Лисп сейчас. Спасибо за помощь, хотя. Оценил. знак равно

Я так не думаю.

Это не так, как работает язык. Вы должны использовать что-то кроме переменных, чтобы обратиться к этой информации о состоянии. Наиболее "натуральный" способ использования свойствthis, Похоже.

Чтобы добавить заметку на эту тему:

В JavaScript, когда вы используете:

function declaration statement or function definition expression then local variables will have Lexical Scoping.

Function constructor then local variables will refer to the global scope (top-level code)

this is the only built-in object in JavaScript that has a dynamic scoping and is set through the execution (or invocation) context.

Итак, чтобы ответить на ваш вопрос, в JSthis это уже динамически ограниченная функция языка, и вам даже не нужно подражать другой.

 Aadit M Shah11 мая 2013 г., 03:03
Я рад знать,this, Спасибо.
Решение Вопроса

Поиск атрибутов проходит через цепочку прототипов, которая вполне соответствует динамическим областям. Просто передайте свою собственную среду динамически изменяемых переменных для использования вместо использования лексической области видимости Javascript.

// Polyfill for older browsers.  Newer ones already have Object.create.
if (!Object.create) {
  // You don't need to understand this, but
  Object.create = function(proto) {
    // this constructor does nothing,
    function cons() {}
    // and we assign it a prototype,
    cons.prototype = proto;
    // so that the new object has the given proto without any side-effects.
    return new cons();
  };
}
// Define a new class
function dyn() {}
// with a method which returns a copy-on-write clone of the object.
dyn.prototype.cow = function() {
  // An empty object is created with this object as its prototype.  Javascript
  // will follow the prototype chain to read an attribute, but set new values
  // on the new object.
  return Object.create(this);
}
// Given an environment, read x then write to it.
function g(env) {
  console.log(env.x);
  env.x = 2;
}
// Given an environment, write x then call f with a clone.
function f(env) {
  env.x = 3;
  g(env.cow());
}
// Create a new environment.
var env = new dyn();
// env -> {__proto__: dyn.prototype}
// Set a value in it.
env.x = 1;
// env -> {x: 1}  // Still has dyn.prototype, but it's long so I'll leave it out.

f(env.cow());
// f():
//   env -> {__proto__: {x: 1}}  // Called with env = caller's env.cow()
//   > env.x = 3
//   env -> {x: 3, __proto__: {x: 1}}  // New value is set in current object
//   g():
//     env -> {__proto__: {x: 3, __proto__: {x: 1}}}  // caller's env.cow()
//     env.x -> 3  // attribute lookup follows chain of prototypes
//     > env.x = 2
//     env -> {x: 2, __proto__: {x: 3, __proto__: {x: 1}}}

console.log(env.x);
// env -> {x: 1}  // still unchanged!
// env.x -> 1
 08 апр. 2012 г., 21:06
@AaditMShah прокомментировал. И иди почитайIntroduction to Object-Oriented JavaScript.
 Aadit M Shah08 апр. 2012 г., 10:03
Ваш код очень запутанный. Можете дать содержательные комментарии, чтобы объяснить, что вы делаете?
 Aadit M Shah09 апр. 2012 г., 04:09
Спасибо, что поделились этой ссылкой со мной тоже - хотя я не узнал ничего, о чем я уже не знал, это был хороший жест. Оценил.
 09 апр. 2012 г., 16:51
@AaditMShah Рад помочь. Собственно, моя первая мысль (используяwith расширить сферу) не работает & # x2026; что все к лучшему, так как это ужасная особенность Javascript :)
 Aadit M Shah09 апр. 2012 г., 04:06
Хорошо, я наконец понял твой код. Вы используете конструктор под названиемdyn симулировать глобальный охват. Каждая функция имеет формальный параметрenv что эквивалентно объекту активации этой функции. этоenv Объект предоставлен вызывающим абонентом. Для функцииf мы поставляем глобальный экземплярdyn, Заg мы поставляем экземпляр глобального копированияdyn толкая первый на прототип цепи. Это интуитивно понятный ответ, и поиск в области видимости действительно напоминает обход цепочки прототипов. Плюс один для поиска альтернативы вместо использованияeval. =)

Почему никто не сказалthis?

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

function called_function () {
   console.log(`My env ${this} my args ${arguments}`, this, arguments);
   console.log(`JS Dynamic ? ${this.jsDynamic}`);
}

function calling_function () {
   const env = Object.create(null);
   env.jsDynamic = 'really?';

   ... 

   // no environment
   called_function( 'hey', 50 );

   // passed in environment 
   called_function.bind( env )( 'hey', 50 );

Возможно, стоит упомянуть, что в строгом режиме все функции не имеют «среды». отправлено им по умолчанию (this нулевой). В нестрогом режиме глобальный объект используется по умолчаниюthis значение для вызываемой функции.

 14 июн. 2015 г., 17:55
Я думаю, вы могли видеть это таким образом.
 Aadit M Shah12 июн. 2015 г., 20:11
Люди уже сказалиthis: Arman, Thilo, Если вы думаете об этомthis это просто еще один аргумент. Таким образом, вы действительно не нуждаетесьthis или. Просто используйте дополнительный параметр. Это именно то, чтоephemient описывает в своем ответе и это причина, по которой я его принял. Ваш ответ не приносит никакой пользы.

Ваш ответ на вопрос