Lisp? Scheme? Erlang, Haskell? Они забыты! Наиболее широко известный язык функционального программирования  это Javascript. JavaScript маскируется под процедурный язык до тех пор пока вы не будете готовы перейти на новый уровень понимания.


Процедурные функции

В начале были функции…

 

function hello(who) {
   alert('Hello '+who);   // Выведет "Hello world"
}

hello('world');

 

Почти каждый язык программирования имеет конструкцию подобную этой. Каждый программист в мире знаком  с этим синтаксисом. "hello" это имя нашей функции, но что произойдет, если мы создадим helloтак же, как и переменную?

 

Функции как переменные

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

 

var hello = function (who) {
   alert('Hello '+who);      // вызовет окошко с надписью "Hello world"
}

hello('world');

 

Тут нет большой разницы, просто добавьте  слово var, знак равенства и сдвиньте hello, все остальное остается точно также. Мы создаем разными путями одну и туже функцию. Взгляните на это определение еще раз. Это больше, чем просто визуальное “удобство” определения функции. Эта конструкция на самом деле что-то значит. А значит она то, что наша функция будет следовать тем же правилам и способностям, как и любая другая переменная в JavaScript.

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

 

Передача функций  функциям

Если мы объявляем “hello” как переменную, то мы можем передать ее как параметр для функции.

 

var say = function(what) {
   what('say function');
}

var hello = function (who) {
   alert('Hello '+who);      // выведет "Hello say function"
}

say(hello); 

 

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

Есть два способа использования функции в качестве аргумента. Вы можете передать указатель на саму функцию, или вы можете выполнить функцию, которая сама вернет функцию. В приведенном выше примере передается указатель “hello” в переменную “what”  в функции “say”. “what” стала указателем на“hello”.

Если вы когда-либо имели дело с событиями в JavaScript, то вы вероятно уже использовали концепцию “функция как указатель”, потому что все обработчики событий требуют указатель на функцию, которая вызывается, когда они срабатывают. Например:

 

function doSomething() {
   alert("Вы нажали на кнопку мыши!");
}

document.onclick=doSomething;

 

Этот пример “вешает” обработчик  “doSomething” на событие onclick. Потому что мы используем лишь имя функции а не ее вызов. С другой стороны если бы “doSomething()” тоже возвращала функцию то все по прежнему бы работало хорошо.

 

function pushedMyButton() {
   alert("Вы нажали на кнопку мыши!");
}

function doSomething() {
   return pushedMyButton; // вернет указатель на  pushedMyButton
}

document.onclick=doSomething();

 

Анонимные функции

 Скорее всего, если вы когда-либо писали стандартный Ajax или обработчик событий в JavaScript, вы знакомы с анонимной функции. Например:

 

document.onmousedown = function() { alert("Вы нажали кнопку мыши"); }

 

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

Это приводит нас к другой концепции функционального программирования - функция гораздо ближе к данным, чем мы могли предположить.  Для того чтобы научится правильно пользоваться функциями в JavaScript их нужно воспринимать как обычные данные(число, строка, массив)

 

Само-вызов функции

 Посмотрите на следующий код

 

var x = (function () {return(5+6)})();
document.writeln(typeof(x));        // Результат: Number
document.writeln(x);                // Результат: 11;

 

Обратите внимание что "x" будет содержать не анонимную функцию, а результат ее работы. Анонимная функция существовала ровно столько чтобы передать результат своей работы в переменную “x”.

 

Вот более реальный пример:

( function () { 
    var s=document.createElement("script"); 
        s.src="http://mydomain.com/script.js"; 
    document.body.appendChild(s);
  } 
) ();

 

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

 

Вы также можете использовать само-вызов анонимной функции когда вам нужно сделать обработку каких-либо данных прежде чем подать их в функцию.

 

 

function doAlert(msg) {
   alert(msg);
}

doAlert( (function(animal1,animal2) { return animal1 + ' loves ' + animal2; } ) ('cat', 'dog') );

 

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

 

function doDisplay(msg) {
   document.writeln(msg);
}

function buildString(num1, num2) {
   return num1 + ' + ' + num2 + ' = ' + (num1+num2);
}

for (i=0; i<10; i++) {
  doDisplay( (buildString) (i, i+5) );
}

 

Что эквивалентно традиционному вызову функций в других языках программирования

 

function doDisplay(msg) {
   document.writeln(msg);
}

function buildString(num1, num2) {
   return num1 + ' + ' + num2 + ' = ' + (num1+num2);
}

for (i=0; i<10; i++) {
  doDisplay( buildString(i, i+5) );

 

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

 

(function($){
  $(body).find('div.block').show(); // ограничили $ внутри функции и работаем с jQuey
})(jQuery)
$('idblock').innerHTML = 5; // а тут спокойно работаем с prototype

 

Большинство плагинов jQuery написаны подобным образом, что позволяет применять их максимально совместимо с другими фреймворками. Но разумеется ничего не мешает вам писать в том же стиле и на prototype.

 

Понимание указателя на функцию

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

 

var originalFunction = function () {
   alert('hello world');
}

var copiedFunction = originalFunction;

var originalFunction = function () {
   alert('goodbye world');
}

 

Переменная copiedFunction это указатель на блок памяти в котором лежит функция originalFunction "hello world" . Когда мы позже изменяем оригинальную функцию на 'goodbyeworld' то copiedFunction возвращает указатель на оригинальную функцию "hello world".

 

Область видимости функции

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

Функций, определенные в теге <script>  являются глобальными, доступны для любой функции, объекта, массива, и под-функции независимо от их местоположения в коде. Переменные в JavaScript, имеют глобальную область видимости, с одной оговоркой - переменные, определенные внутри функции являются локальными к этой функции и глобальными к ее под-функциям или потомкам.

 

var globalFunction = function () {    // глобальная функция, доступна везде
   alert('hello world');
}

var containerFunction = function() { // глобальная функция, доступна везде

  var subFunction = function() {    //приватная функция, доступна только для containerFunction или ее потомков .
    alert('Private');
    globalFunction();              // Мы имеем доступ к глобальным функциям .
  }

  globalFunction();                 
  subFunction()                     .
}

containerFunction();                 
subFunction(); // этот вызов невозможен потому что
               subfunction () существует только внутри containerFunction();

 

Предостережение: Если вы объявляете новую переменную без ключевого слова “var” то такая переменная является одним из свойств глобального объекта windowи доступна глобально.

 

var globalFunction = function () {    // глобальная функция, доступна везде
   alert('hello world');
}

var containerFunction = function() { // глобальная функция, доступна везде

  subFunction = function() {        // глобальная везде (так как не использовалось var)
    alert("I'm Private");
    globalFunction();               
  }

  globalFunction();                 
  subFunction()                     // не вызовет ошибки.
}

containerFunction();                 
subFunction(); // также не вызовет ошибки

 

Кроме того функцию можно вызвать как window.subFunction();

 

Функция как Объект

 Все функции в JavaScriptэто объекты. Массив, число и строка также являются объектами.

 

var myArray = [];
    myArray[1] = 1;
    myArray[2] = 2;
    myArray[3] = 3;
    myArray.one = 'one';
    myArray.two = 'two';
    myArray.three='three';
    for (i=0; i < myArray.length; i++) {
       document.writeln(myArray[i]);
    }

 

myАггау является объектом, который имеет свойства и методы, общие для всех массивов и который можно расширить. Когда мы перебираем в цикле myArray мы получим только [1] [2] [3] (1,2,3), свойства  ('one','two','three') не являются частью массива они лишь свойства его объекта. Чтобы не выйти за границы массива myArray мы использовали свойство .length чтобы найти количество элементов в нем.

Таким образом, массив в JavaScript существуют в двух уровнях. На вершине мы имеем сам массив, а ниже объект, который можно использовать для хранения данных в его свойствах и создавать любые методы, которыми мы хотим в дополнение к существующим методам и свойствам всех  массивов (такие как .length , .sort, .concat и т.д.).

Функции в JavaScript, подобны массивам , на высшем уровне есть сама функция. Она делает то, что мы определили ей  делать. Но под этим идет объект функции, этот объект имеет общие свойства и методы, и эти свойства и методы могут быть расширены нами, по мере необходимости.

 

Функция Объекты и методы

Каждая функция обладает следующими свойствами:

arguments - массив / объект, содержащий аргументы, передаваемые функции

arguments.length – Хранит число аргументов в массиве

arguments.callee - указатель на выполняемую функцию (позволяет использовать анонимные функции рекурсивно).

length - число ожидаемых аргументов функции  myFunc (х, у) = 2.

constructor - указатель на функцию конструктор.

prototype - позволяет создавать прототипы функций для всех объектов.

 

Каждая функция имеет следующие методы:

apply - метод, который позволяет более просто передать аргументы функции(аргументы подаются виде массива, и при ‘том позволяет сменить глобальную область видимости функции т.е. позволяет вызывать функции в другом контексте).

call – тоже самое что и apply, только аргументы подаются по отдельности как и в обычную функцию.

toSource - возвращает исходный текст функции в виде строки.

toString - возвращает исходный текст функции в виде строки.

valueOf - возвращает исходный текст функции в виде строки.

 

Понимание аргументов функции

Если есть объявленная функция

 

var myFunc = function(a,b,c) { }

 

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

 

myFunc(1);
myFunc(1,2);
myFunc(1,2,3);
myFunc(1,2,3,4,5,6,7,8,9,10);

 

Если была вызвана myFunc (1) то b и c будет типа ‘undefined’ и вы можете увидеть это несколькими способами.

 

var myFunc = function(a,b,c) {
   if (!a) {a=1};
   b = b || 2;
   if (c==undefined) {c=3;}
   document.writeln(a+'< br >'+b+'< br >'+c+'< BR  >'); 
}
myFunc(1);

 

 Как вы видите, ‘undefined’ является ложное условие, таким образом Вы можете использовать логические операторы, чтобы обнаружить, если аргумент не был установлен и исправить это. Третий способ проверки является самым безопасным для нахождения неопределенных аргументов, так как 1 и 2 некорректно сработают на значения false,’’ и 0.

 

.length и arguments.length свойства позволяют проверить на ожидаемое количество аргументов и количество аргументов, которое на самом деле прошло. Например:

 

var myFunc = function(a,b,c) {
   document.writeln('expected '+myFunc.length+' arguments.< BR >');  //Outputs: 3
   document.writeln('got ' + arguments.length+ ' arguments.< BR >'); //Outputs: 1
}
myFunc(1);

 

Как вы можете видеть, функция ожидает 3 аргумента, но получила только 1!

 

Использование массива arguments

В нашем примере выше a,b, и c представлены переменными, которые мы использовали, чтобы получить первые три аргумента. Если аргументов будет меньше чем ожидалось то переменные будут установлены как "undefined", если там будет больше чем ожидалось они будут по-прежнему доступны для нас в массиве arguments

 

var myFunc = function() {
   document.writeln(arguments.length+'< BR >');   // displays 10
   for(i=0; i < arguments.length; i++) {
      document.writeln(arguments[i]+',');       // displays: 1,2,3,4,5,6,7,8,9,10,
   }
}
myFunc(1,2,3,4,5,6,7,8,9,10);

 

Опыт показывает что массив arguments имеет не все свойства и методы доступные обычным массивам поэтому если вы хотите использовать методы (sort, splice, slice, и т.д.) то нужно сделать следующее:

 

var args = Array.prototype.slice.apply(arguments);

 

Как передать аргументы одной функции другой

 Время от времени, кто-то спрашивает, как передать аргументы одной функции в качестве аргументов для другой. Например:

 

var otherFunc=function() {
   alert(arguments.length); // вернет 1 потому что как параметр ему подается лишь 1 массив
}
   
var myFunc = function() {
  alert(arguments.length); // вернет 10
  otherFunc(arguments); // подаем аргументы единым массивом
}
myFunc(1,2,3,4,5,6,7,8,9,10);

 

Здесь подается 10 аргументов в myFunc и myFunc пытается передать их otherFunc но вместо этого передает его аргументы в виде массива, а не как  список аргументов. Мы можем обойти это  с вызовом метода apply, которая будет передавать массив аргументов как список аргументов в новую функцию

 

var otherFunc = function() {
   alert(arguments.length); // Вернет 10
}
   
var myFunc = function() {
  alert(arguments.length); // Вернет: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);

 

Как использовать рекурсию на анонимных функциях

Допустим, у нас есть анонимные функции факториала, и мы хотим сделать это рекурсивно. Как мы вызовем функцию без имени? Хорошо что в JavaScript arguments.callee свойство содержит указатель на выполняемую в данный момент функцию, это означает, что анонимная функции может, действительно, вызывать сама себя.

 

alert((function(n){ if(n <= 1){return 1;}else{return n*arguments.callee(n-1);}})(10));

 

Заключение

Можно далеко уйти в объектно-ориентированное программирование и объект Function Javascript, но это тема для другой  статьи. Сейчас, однако, я надеюсь, что эта статья помогла вам понять мощь и гибкость JavaScript функций, и позволит Вам использовать для своих целей программирование на JavaScript.

Рассказать друзьям

Добавить комментарий


Защитный код
Обновить

Комментарии   

0
AntohaFric
# AntohaFric 24.11.2010 18:03
Ну так то здорово но все равно нифига не понятно(
0
asheee
# asheee 12.05.2012 13:22
да, это не ФП
0
Leroy
# Leroy 15.06.2012 20:38
качаем и комментируем!
0
Дмитрий
# Дмитрий 09.09.2012 15:04
Добрый день! Можете ли помочь с данной проблемой? У нас сай на shop-script, не знаю как закачать прайс лист с обновленными ценами на сайт, чтобы в админке не обновлять в ручную. можно ли установить?

Спасибо! С Уважением Дмитрий.
0
Leroy
# Leroy 11.09.2012 12:27
извините, здесь Danneo CMS, с шоп-скрипт не работал уже очень давно, он ведь платный
0
Vova
# Vova 07.11.2012 20:16
Да уж, и в http://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B8_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F#.D0.9D.D0.B5.D0.BA.D0.BE.D1.82.D0.BE.D1.80.D1.8B.D0.B5_.D1.8F.D0.B7.D1.8B.D0.BA.D0.B8_.D1.84.D1.83.D0.BD.D0.BA.D1.86.D0.B8.D0.BE.D0.BD.D0.B0.D0.BB.D1.8C.D0.BD.D0.BE.D0.B3.D0.BE_.D0.BF.D1.80.D0.BE.D0.B3.D1.80.D0.B0.D0.BC.D0.BC.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F о JavaScript ни слова.
0
Андрей
# Андрей 11.04.2013 06:21
Добрый день. Хочу добавить на сайт новую функцию, хотел бы ознакомиться с вашим примером.
0
Leroy
# Leroy 11.04.2013 14:58
не понял, на какой сайт вы хотите добавить, и что?
0
Денис
# Денис 23.05.2013 10:56
Без комментариев
0
zomer1972
# zomer1972 18.07.2013 18:31
без комментариев, их пока нет..
0
Leroy
# Leroy 19.07.2013 03:43
надеюсь будут)
0
Владимир
# Владимир 28.07.2013 16:39
*Заблочил баннер , мне норм :D
0
Leroy
# Leroy 29.07.2013 11:38
какой баннер?
0
Diman
# Diman 11.08.2013 17:17
Респект не знал, оч облягчает жизнь
0
kapraus
# kapraus 17.10.2013 18:28
Привет
0
Медвед
# Медвед 17.10.2013 20:05
Привед, Медвед... )))
0
Leroy
# Leroy 17.10.2013 21:11
Дискуссия?!)
0
SerLee
# SerLee 05.11.2013 22:57
Уберите этот баннер с грустным котом, пожалуйста - никто не станет смотреть вашу рекламу, а вот если захочет помочь - просто подкинет несколько рублей.
0
Leroy
# Leroy 05.11.2013 22:58
прям так сильно мешает? за все время ни разу не подкинули рублей. я не жалуюсь. но котенок как-то больше забавляет
0
green-zip
# green-zip 06.11.2013 15:24
Вот так всё и начинается…

Незаметно произрастают в молодой неопытной Душе ростки безразличия и чванства.

Сначала "ЗАБАВЛЯЕТ" грустный, голодный котёнок,

потом станет забавлять несчастная, голодная бабушка,

собирающая на помойке грязные буржуазные объедки,

ну, и, наконец, вообще обхохочешься от вида сожжённого на Вечном Огне бомжа...

Вот так всё и начинается… Но этим всё и заканчивается…
0
Biturbo002
# Biturbo002 11.11.2013 18:00
Добавил "отключи адблок" в фильтры :D мне тоже норм
0
Leroy
# Leroy 11.11.2013 23:57
держите нас в курсе
0
Daniel
# Daniel 06.01.2014 01:23
Зачем выкладывать нерабочие исходники? Сложно проверить работоспособность, перед тем, как выложить?
0
Leroy
# Leroy 06.01.2014 05:01
этим исходникам года три уже. на момент публикации были рабочими. Ну а вообще их назначение лишь в демонстрации работы с библиотекой
0
Leroy
# Leroy 06.01.2014 05:03
за это время структура яндекс картинок пять раз уже поменялась
0
daniel
# daniel 06.01.2014 10:23
Было бы более удобно, если бы в описании добавил фразу на подобии: исходники рабочие на момент 2 февраля 2010 года. Это не в упрек, а так.. пожелание. Просто когда запускаешь скрипт, думаешь, то ли я что-то начудил, то ли структура изменилась и надо уже в эту сторону копать.
0
LaFey
# LaFey 07.01.2014 23:30
а включить мозги слабо?
0
Пользователь
# Пользователь 11.03.2014 14:18
Еще вопросик, не отображается бегунок регулировки прозрачности заливки полигона.
0
Leroy
# Leroy 12.03.2014 19:35
какой браузер?
0
diego
# diego 31.03.2014 01:35
My test coment
0
stakin-ap
# stakin-ap 03.04.2014 12:40
привет
0
Almina
# Almina 04.04.2014 21:40
неработает :(
0
rizhikov
# rizhikov 05.04.2014 03:38
супер
0
Артем
# Артем 10.06.2014 13:26
Vita brevis, ars - longa!
0
Игорь Ширин
# Игорь Ширин 17.06.2014 13:33
ё1имь
0
Leroy
# Leroy 17.06.2014 14:27
отличное начало)
0
Владимир
# Владимир 11.07.2014 17:52
Буду рад если меня поддержат.
0
Свят Серов
# Свят Серов 14.07.2014 01:42
Учитесь, и оно вам пригодиться, не сегодня, а завтра, а может и твоим внукам!
0
dimmm2001
# dimmm2001 07.08.2014 22:30
Карты яндекс
0
Elena_Zverskaj
# Elena_Zverskaj 21.09.2014 01:05
Да код не рабочий((((
0
Олег
# Олег 20.11.2014 19:02
Прошу дать доступ, очень интересно посмотреть как работает конструктор карт.
0
Leroy
# Leroy 21.11.2014 09:42
вы про какой конструктор? этот http://maps.xdan.ru/ ?
0
summy
# summy 15.12.2014 18:33
Автор - вор! Скрипт на хабре был аж в 2009 году
0
Leroy
# Leroy 16.12.2014 11:54
Да, я тоже слышал. Он на своем блоге про jQuery пишет, так вот, не он ее написал. не верьте ему
0
parm.mx
# parm.mx 20.12.2014 19:36
Добрый день!
0
Александр
# Александр 02.01.2015 21:29
Комментарий обязательно?
0
Oleg Deev
# Oleg Deev 14.03.2016 15:07
Спасибо, очень содержательно и просто. Жаль, не вижу имени автора.