Выразительный JavaScript (Хавербеке) - страница 123

возвращает функцию, добавляющую её аргумент к аргументу >f, то есть, ей нужен доступ к локальной области видимости внутри >f для использования переменной >a.

>run("do(define(f, fun(a, fun(b, +(a, b)))),",

>    "   print(f(4)(5)))");

>// → 9

Объясните, используя определение формы >fun, какой механизм позволяет этой конструкции работать.

Комментарии

Хорошо было бы иметь комментарии в Egg. К примеру, мы могли бы игнорировать оставшуюся часть строки, встречая символ >\# – так, как это происходит с >// в JavaScript.

Большие изменения в парсере делать не придётся. Мы просто поменяем >skipSpace, чтобы она пропускала комментарии, будто они являются пробелами – и во всех местах, где вызывается >skipSpace, комментарии тоже будут пропущены. Внесите это изменение.

>// Поменяйте старую функцию

>function skipSpace(string) {

>  var first = string.search(/\S/);

>  if (first == -1) return "";

>  return string.slice(first);

>}


>console.log(parse("# hello\nx"));

>// → {type: "word", name: "x"}


>console.log(parse("a # one\n   # two\n()"));

>// → {type: "apply",

>//    operator: {type: "word", name: "a"},

>//    args: []}

Чиним область видимости

Сейчас мы можем присвоить переменной значение только через >define. Эта конструкция работает как при присвоении старым переменным, так и при создании новых.

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

Добавьте форму >set, схожую с >define, которая присваивает переменной новое значение, обновляя переменную во внешней области видимости, если она не задана в локальной. Если переменная вообще не задана, швыряйте >ReferenceError (ещё один стандартный тип ошибки).

Техника представления областей видимости в виде простых объектов, до сего момента бывшая удобной, теперь будет вам мешать. Вам может понадобиться функция >Object.getPrototypeOf, возвращающая прототип объекта. Также помните, что область видимости не наследуется от >Object.prototype, поэтому если вам надо вызвать на них >hasOwnProperty, придётся использовать такую неуклюжую конструкцию:

>Object.prototype.hasOwnProperty.call(scope, name);

Это вызывает метод >hasOwnProperty прототипа >Object и затем вызывает его на объекте >scope.

>specialForms["set"] = function(args, env) {

>  // Ваш код

>};


>run("do(define(x, 4),",

>    "   define(setx, fun(val, set(x, val))),",

>    "   setx(50),",

>    "   print(x))");

>// → 50

>run("set(quux, true)");