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.

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

Платная консультация по вопросам 1500 руб/час

Прочитали статью и остались вопросы? Меня зовут Валерий и я её автор. С радостью объясню Вам в скайпе все затруднительные моменты, которые остались за рамками статьи!

Подробнее ...

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


Комментарии   

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

Спасибо! С Уважением Дмитрий.
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 11.09.2012 12:27
извините, здесь Danneo CMS, с шоп-скрипт не работал уже очень давно, он ведь платный
Ответить | Ответить с цитатой | Цитировать
Vova
0 # 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
Добрый день. Хочу добавить на сайт новую функцию, хотел бы ознакомиться с вашим примером.
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 11.04.2013 14:58
не понял, на какой сайт вы хотите добавить, и что?
Ответить | Ответить с цитатой | Цитировать
Денис
0 # Денис 23.05.2013 10:56
Без комментариев
Ответить | Ответить с цитатой | Цитировать
zomer1972
0 # zomer1972 18.07.2013 18:31
без комментариев, их пока нет..
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 19.07.2013 03:43
надеюсь будут)
Ответить | Ответить с цитатой | Цитировать
Владимир
0 # Владимир 28.07.2013 16:39
*Заблочил баннер , мне норм :D
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 29.07.2013 11:38
какой баннер?
Ответить | Ответить с цитатой | Цитировать
Diman
0 # Diman 11.08.2013 17:17
Респект не знал, оч облягчает жизнь
Ответить | Ответить с цитатой | Цитировать
kapraus
0 # kapraus 17.10.2013 18:28
Привет
Ответить | Ответить с цитатой | Цитировать
Медвед
0 # Медвед 17.10.2013 20:05
Привед, Медвед... )))
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 17.10.2013 21:11
Дискуссия?!)
Ответить | Ответить с цитатой | Цитировать
SerLee
0 # SerLee 05.11.2013 22:57
Уберите этот баннер с грустным котом, пожалуйста - никто не станет смотреть вашу рекламу, а вот если захочет помочь - просто подкинет несколько рублей.
Ответить | Ответить с цитатой | Цитировать
Leroy
0 # Leroy 05.11.2013 22:58
прям так сильно мешает? за все время ни разу не подкинули рублей. я не жалуюсь. но котенок как-то больше забавляет
Ответить | Ответить с цитатой | Цитировать
green-zip
0 # green-zip 06.11.2013 15:24
Вот так всё и начинается…

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

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

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

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

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

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