Хорошие примеры использования Closure в Javascript - PullRequest
5 голосов
/ 30 января 2012

Ну, я недавно узнал о замыканиях в Javascript.

Хотя я нахожу эту концепцию по-настоящему удивительной, мне еще только предстоит найти для них подходящее приложение.

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

То, что я нигде не могу найти, это примеры, которые заставляют меня задуматься: «Ух ты! Ты можешь сделать ЭТО с помощью замыканий? Потрясающе !!!». Все примеры, которые я нахожу, носят чисто академический характер.

function say667() {
  // Local variable that ends up within closure
  var num = 666;
  var sayAlert = function() { alert(num); }
  num++;
  return sayAlert;
}

var sayNumber = say667();
alert(sayNumber());

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

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

Спасибо

Ответы [ 7 ]

7 голосов
/ 30 января 2012

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

Вот примеры замыканий в ответах, которые я предоставил здесь на SO.

Доступ к родительским локальным переменным из обратного вызова setTimeout: https://stackoverflow.com/a/7032671/816620

Передача нестатической информации в отложенный обратный вызов: https://stackoverflow.com/a/8660518/816620

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

И вот полезное закрытие, которое создает частныйпеременная:

function slides(images) {
    var slideImages = images || [];

    // because of this closure, the variable slideImages is available
    // to the method defined in here even though the slides function
    // has already finished executing
    this.addSlide = function(url) {
        slideImages.push(url);
    }
    this.clearSlides = function() {
        slideImages = [];
    }
}

// the slideImages variable is not available out here
// it is truly private inside the clsoure
var slideshow = new slides(imgArray);
slideshow.addSlide("xxx.jpeg");
5 голосов
/ 30 января 2012

переменные каррирования

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

Кажется, что людям нравится карри.


Создание манипуляторов со значением карри

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

function curry() {
    var args = Array.prototype.slice.call(arguments);
    return function(fn) {
        return function() {
            var args2 = Array.prototype.slice.call(arguments);
            return fn.apply(this,args.concat(args2));
        };
    };
}

Создание функции, которая карри строки

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

var workWithName = curry("Bubba");

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


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

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

var talkToName = workWithName(function(curried_str, before, after) {
    return before + curried_str + after;
});

Так что теперь у меня есть функция talkToName, которая принимает 2 строки, которые обертывают строку карри.

talkToName("Hello there ", ". How are you?"); // "Hello there Bubba. How are you?"
talkToName("", " is really super awesome.");  // "Bubba is really super awesome."

Обратите внимание , что я передаю два аргумента в функцию talkToName, но функция, переданная workWithName, принимает 3 аргумента.

Первый аргумент передается функцией, которую мы создали из workWithName(), и два аргумента, которые мы передаем talkToName, добавляются после исходного аргумента карри.


Создание функции приращения символов строки карри

Здесь я создаю совершенно другую функцию, используя оригинальную функцию workWithName, которая будет принимать строку «Bubba» и возвращать строку с буквами, увеличенными на заданное значение ...

var incrementName = workWithName(function(curried_str, n) {
    var ret = '';
    for(var i = 0; i < curried_str.length; i++) {
        ret += String.fromCharCode(curried_str[i].charCodeAt() + n);
    }
    return ret;
});

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

incrementName(3);  // "Exeed"
incrementName(8);  // "J}jji"
incrementName(0);  // "Bubba"

Итак, вы можете видеть, что мы дали curry() значение, и оно вернуло нам функцию, которую можно использовать для создания новых функций, которые работают с исходным значением.

Еще раз обратите внимание , что я передаю один аргумент функции incrementName, но функция, переданная workWithName, принимает 2 аргумента. Первый аргумент карри.


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

Вот пример, который создает генератор функций, который работает с числами 3 и 5.

var workWith3And5 = curry(3, 5);

Создание функций, которые делают разные вещи с каррированными числами

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

var addNTo3And5 = workWith3And5(function(x, y, n) {
    return [3 + n, 5 + n];
});

addNTo3And5( 8 );  // [11, 13];
addNTo3And5( -4 ); // [-1, 1];

И еще одна, использующая ту же самую функцию workWith3And5, которая каррирует числа 3 и 5, которая создает массив массивов 3 x 5, где вложенному массиву присваивается некоторое содержимое ...

var create3By5GridWithData = workWith3And5(function(x, y, data) {
    var ret = []
    for(var i = 0; i < x; i++) {
        ret[i] = [];
        for(var j = 0; j < y; j++) {
           ret[i][j] = data;
        }
    }
    return ret;
});

create3By5GridWithData( 'content' ); // [Array[5], Array[5], Array[5]]
5 голосов
/ 30 января 2012

Базовый пример:

var getDay = (function () {
    var days = [
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday',
        'Sunday'
    ];

    return function ( n ) {
        return days[ n - 1 ];
    };
}());

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

Итак, целью IIFE в вышеприведенном примере было определение массива days, который действует как приватная переменная getDay функции.

5 голосов
/ 30 января 2012

Хорошо, одна из замечательных вещей, которые вы можете сделать, это иметь личные переменные:

function Thing() {
  var x = 10;
  this.getX = function () {
    return x;
  }

  this.increment = function () {
    x++;
  }
}

Теперь, когда вы создаете new Thing, у него будет метод getX и increment, но нет способа снизить значение x. Это значение x также будет уникальным для экземпляра Thing.

У Крокфорда есть страница об этом шаблоне: http://javascript.crockford.com/private.html

3 голосов
/ 30 января 2012

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

JLinx.Delegate=function() {
  var validateArg=function(arg) {
    if(typeof arg!=="string"&&typeof arg!=="function"&&arg!==null&&arg!==undefined) {
      throw new ArgumentException("arg");
    }
  };
  var funcBody;
  function prepBody(code,returnsResult) {
    var temp=code.trimLeft().trimRight();
    if(returnsResult&&temp.indexOf("return ")== -1) {temp="return "+temp;}
    if(temp.substr(temp.length-1,1)!=";") {temp+=";";}
    return temp;
  }
  function getDelegate(arg,defaultLambda,returnsResult) {
    validateArg(arg);
    if(typeof arg==="function") {return arg;}
    arg=(arg===null||arg===undefined)?defaultLambda:arg;
    if(arg.indexOf("=>")> -1) {
      var parts=arg.split("=>");
      var argList=parts[0];
      funcBody=prepBody(parts[1],returnsResult);
      argList=argList.trimLeft().trimRight()==""?"e":argList;
      argList=(argList.indexOf(",")> -1)?argList.split(","):[argList];
      switch(argList.length) {
        case 1:
          return new Function(argList[0],funcBody);
        case 2:
          return new Function(argList[0],argList[1],funcBody);
        default:
          throw new InvalidOperationException("Invalid number of arguments to action delegate.");
      }
    }
    else {
      funcBody=prepBody(arg,returnsResult);
      return new Function("e",funcBody);
    }
  }
  var factory=
    {
      actionFrom: function(arg) { return getDelegate(arg,"e => return;",false); },
      accumulatorFrom: function(arg) { return getDelegate(arg,"e, v => return v;",true); },
      comparerFrom: function(arg) { return getDelegate(arg,"l,r=>return l<r?-1:l>r?1:0;",true); },
      joinSelectorFrom: function(arg) { return getDelegate(arg,"o, i = { return { o : o, i : i }; };",true); },
      predicateFrom: function(arg) { return getDelegate(arg,"e => return true;",true); },
      selectorFrom: function(arg) { return getDelegate(arg,"e => return e;",true); }
    };
  return factory;
} ();

Я знаю, что это не так уж много, но что позволяет вам делать с другими методами вбиблиотека (которая на самом деле предоставляет LINQ-to-XML) записывает следующее:

var exists = myXmlElement.Any("e.name == 'foo' || e.name == 'bar';');

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

Это одна вещь, которую вы можете сделать с замыканиями.

3 голосов
/ 30 января 2012

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

1 голос
/ 19 февраля 2014

Мне кажется, мне очень нравится этот пример, чтобы объяснить замыкания в Javascript ..

      var juice = "Mango";
    var foo = function() {
     var juice = "Apple";
  return function(){
   return juice;
}

    };
var juicebar = foo();
console.log(juice);
console.log(juicebar());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...