Лексические параметры видимости в javascript

Работая через Javascript Koans, я зацикливался на следующем коде:

    it("should use lexical scoping to synthesise functions", function () {

function makeMysteryFunction(makerValue)
{
  var newFunction = function doMysteriousThing(param)
  {
    return makerValue + param;
  };
  return newFunction;
}

var mysteryFunction3 = makeMysteryFunction(3);
var mysteryFunction5 = makeMysteryFunction(5);

expect(mysteryFunction3(10) + mysteryFunction5(5)).toBe(FILL_ME_IN);
});

Он получает 23, и это нормально. Я озадачен тем, как / почему параметр, переданный переменной 'mysteryFunction3', передается в функцию doMysteriousThing как 'param'.

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

    var mysteryFunction3 = makeMysterFunction(3);

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

   mysteryFunction3(10)

будет ли этот параметр (10) считываться как параметр для внутренней функции?

3 ответа

Поначалу я тоже пытался понять это. Вот как я достиг ясности с ударом за ударом. Это мой первый пост в стеке, и я прошу прощения за то, что я растянул его.

Возьмем очень простую функцию, возвращающую строку:

      function fun() {
       return 'Hi!';
    }

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

      console.log(fun); //logs ---> [Function: fun] 

Если мы зарегистрируем его снова, но с круглой скобкой вызова:

      console.log(fun()); //logs ---> Hi!

вызов функции равен ее возвращаемому значению:

      console.log(fun() === 'Hi!'); //logs ---> true

Исходя из этого, давайте перепишем нашу функцию, чтобы в ней была объявлена ​​другая функция, возвращающая строку. Внешняя функция вернет вызов внутренней функции:

      function funAgain() {
   function innerFun() {
     return 'Hello there!';
   }
   return innerFun();
}

Итак, в рамках funAgain (innerFun () === 'Hello there!') Оценивается как true, поэтому, когда мы регистрируем вызов funAgain в консоли:

      console.log(funAgain()); //logs ---> 'Hello there!'

Но что, если мы не будем использовать вызывающие скобки для innerFun в операторе возврата внешней функции?

      function funAgain() {
  function innerFun() {
    return 'Hello there!';
  }
  return innerFun;
}

console.log(funAgain()); //logs [Function: innerFun]

Возвращается функция САМ. Хотя на самом деле это еще не вся история, мы могли бы подумать о (funAgain () === innerFun). Очевидно, вы не можете фактически запустить это сравнение на практике из-за проблем с областью видимости (innerFun не может существовать вне вызова funAgain) . НО! Давайте на мгновение подумаем об этом так. Это означает, что если мы зафиксируем возвращаемое значение funAgain в переменной:

      var innerFunCaptured = funAgain();

console.log(innerFunCaptured); // logs [Function: innerFun]

Мы снова концептуально имеем (innerFunCaptured === innerFun) ...

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

      console.log(innerFunCaptured()); //logs ---> 'Hello there!'

Когда я говорил о «всей истории» выше, я упустил то, что привязка внутренней функции к переменной является результатом вызова внешней функции, поэтому на самом деле привязка включает не только сам innerFun, но ТАКЖЕ и среду, которую она был создан внутри, включая любые потенциальные аргументы, переданные через вызов внешней функции, что позволяет нам ...

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

      function funOnceMore(greetingPartOne) {
  function innerFun(greetingPartTwo) {
    return greetingPartOne + ' ' + greetingPartTwo;
  }
  return innerFun;
}

Что, если мы зарегистрируем funOnceMore с аргументом.

      console.log(funOnceMore('Hello')) //logs ---> [Function: innerFun]

Опять же, возвращается сам innerFun. Но как насчет аргумента HelloPartOne, который мы передали? Что ж, все прошло нормально, но поскольку innerFun никогда не вызывается в funOnceMore, приветствиеPartOne никогда не использовалось каким-либо значимым образом. Нам нужно выяснить, как вызвать innerFun! Ответ: нам нужно привязать его к переменной, как мы это делали на предыдущем шаге.

      var innerFunCapturedAgain = funOnceMore('Hello')

Теперь innerFunCapturedAgain содержит innerFun И среду funOnceMore с аргументом Hello, который мы ему передали.

Итак, теперь мы можем вызвать innerFun, заключив круглые скобки в innerFunCapturedAgain, и эти круглые скобки будут инкапсулировать аргумент для welcomePartTwo, который мы передаем в innerFun.

      console.log(innerFunCapturedAgain('there!')) //logs ---> 'Hello there!'

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

makeMysteryFunction делает функцию, которая добавляет свой аргумент (makerValue) к аргументу, который передается функции, которую он возвращает (mysteryFunctionX).

Итак, что помогло мне понять это преобразование чисел в строки:

function makeGreeting(greeting)
{
  var newFunction = function greet(name)
  {
    return greeting + ' ' + name;
  };
  return newFunction;
}

var makeGreetingHi = makeGreeting('Hi');
var makeGreetingHello = makeGreeting('Hello');
//finally call functions
makeGreetingHi('Stranger');
//Hi Stranger
makeGreetingHello('Friend');
//Hello Friend

Все, что я делал, это менял имя функций и объединял строки вместо добавления чисел. Что сбивает с толку коан, так это названия функций: думаю, это хороший пример плохой практики. Дело в том, что в приведенном мною примере greet функция имеет доступ к greeting, К сожалению, это потеряно в номенклатуре

Что-то, что я нашел очень полезным для понимания того, что именно здесь происходит, - это добавление console.log, чтобы показать содержимое "mysteryFunction3". Так:

var mysteryFunction3 = makeMysteryFunction(3);
var mysteryFunction5 = makeMysteryFunction(5);

console.log(mysteryFunction3);

expect(mysteryFunction3(10) + mysteryFunction5(5)).toBe(FILL_ME_IN);

Вывод консоли был таким:

doMysteriousThing(param)
  {
    return makerValue + param;
  }

Исходя из C# фона, это безумие. Но, по крайней мере, теперь я это понимаю!

Другие вопросы по тегам