3.1 Функции

Язык программирования не может обойтись без механизма многократного использования кода программы. Такой механизм обеспечивается процедурами или функциями. В JavaScript функция выступает в качестве одного из основных типов данных. Одновременно с этим в JavaScript определен класс объектовFunction.

В общем случае любой объект JavaScript определяется через функцию. Для создания объекта используется конструктор, который в свою очередь вводится через Function. Таким образом, с функциями в JavaScript связаны следующие ключевые вопросы:

  • функция как тип данных;
  • функция как объект;
  • функция как конструктор объектов.

Именно эти вопросы мы и рассмотрим в данном разделе.

Функция как тип данных

Определяют функцию при помощи ключевого слова function:

function f(arg1,arg2,...)
 { 
 /* тело функции */ 
 } 

Здесь следует обратить внимание на следующие моменты. Во-первых, function определяет переменную с именем f. Эта переменная имеет тип function:

document.write('Тип переменной f: '+ typeof(f));
// Будет выведено: Тип переменной f: function 

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

var i=5;
 
function f(a,b,c)
{
 if (a>b) return c;
}
 
document.write('Значение переменной i: '+ i.valueOf());
 
// Будет выведено:
// Значение переменной i: 5
 
document.write('Значение переменной f:<BR>'+ f.valueOf());
 
// Будет выведено:
// Значение переменной f:
// function f(a,b,c)
// {
//  if (a>b) return c;
// } 

Как видим, метод valueOf() применим как к числовой переменной i, так и к переменной f, и возвращает их значение. Более того, значение переменной f можно присвоить другой переменной, тем самым создав «синоним» функции f:

  function f(a,b,c)
{
 if (a>b) return c;
 else return c+8;
}
 
var g = f;
alert('Значение f(2,3,2): '+ f(2,3,2) );
alert('Значение g(2,3,2): '+ g(2,3,2) );
 
// Будет выведено:
// Значение f(2,3,2): 10
// Значение g(2,3,2): 10 

Этим приемом удобно пользоваться для сокращения длины кода. Например, если нужно много раз вызвать метод document.write(), то можно ввести переменную: var W = document.write (обратите внимание — без скобок!), а затем вызывать: W(‘<H1>Лекция</H1>’).

Коль скоро функцию можно присвоить переменной, то ее можно передать и в качестве аргумента другой функции.

 function kvadrat(a)
{   return a*a;   }
 
function polinom(a,kvadrat)
{ return kvadrat(a)+a+5;}
 
alert(polinom(3,kvadrat));
// Будет выведено: 17 

Все это усиливается при использовании функции eval(), которая в качестве аргумента принимает строку, которую рассматривает как последовательность операторов JavaScript (блок) и выполняет этот блок. В качестве иллюстрации приведем скрипт, который позволяет вычислять функцию f(f(…f(N)…)), где число вложений функции f() задается пользователем.

<SCRIPT>
function kvadrat(a)
{   return a*a;   }
 
function SuperPower()
{ var
 N = parseInt(document.f.n.value),
 K = parseInt(document.f.k.value),
 L = R = '';
 
 for(i=0; i<K; i++)
 {
  L+='kvadrat(';
  R+=')';
 }
 return eval(L+N+R);
}
</SCRIPT>
 
<FORM NAME=f>
 Введите аргумент (число):
 <INPUT NAME=n><BR>
 Сколько раз возвести его в квадрат?
 <INPUT NAME=k><BR>
 <INPUT TYPE=button value="Возвести" onClick="alert(SuperPower());">
</FORM>
 3.1. Многократное вложение функции kvadrat() в себя 

Обратите внимание на запись L=R=». Она выполняется справа налево. Сначала происходит присваивание R=». Операция присваивания выдает в качестве результата значение вычисленного выражения (в нашем случае — пустая строка). Она-то и присваивается далее переменной L.

Поясним работу скрипта в целом. В функции SuperPower() мы сначала считываем значения, введенные в поля формы, и преобразуем их из строк в целые числа функцией parseInt(). Далее с помощью цикла for мы собираем строку L, состоящую из K копий строки » kvadrat( «, и строку R, состоящую из K правых скобок » ) «. Теперь мы составляем выражение L+N+R, представляющее собой K раз вложенную в себя функцию kvadrat(), примененную к аргументу N. Наконец, с помощью функции eval() вычисляем полученное выражение. Таким образом, вычисляется функция (…((N)2)2…)2.

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

У любого типа данных JavaScript существует объектовая «обертка» (wrapper), которая позволяет применять методы типов данных к переменным и литералам, а также получать значения их свойств. Например, длина строки символов определяется свойством length. Аналогичная «обертка» есть и у функций — это класс объектов Function.

Например, увидеть значение функции можно не только при помощи метода valueOf(), но и используя метод toString():

 function f(x,y)
{
  return x-y;
}
document.write(f.toString()); 

Результат распечатки:

function f(x,y) { return x-y; }

Свойства же функции как объекта доступны программисту только тогда, когда они вызываются внутри этой функции. Наиболее часто используемыми свойствами являются: массив (коллекция) аргументов функции ( arguments[] ), его длина ( length ), имя функции, вызвавшей данную функцию ( caller ), и прототип ( prototype ).

Рассмотрим пример использования списка аргументов функции и его длины:

  function my_sort()
{
  a = new Array(my_sort.arguments.length);
  for(i=0;i<my_sort.arguments.length;i++)
     a[i] = my_sort.arguments[i];
  return a.sort();
}
 
b = my_sort(9,5,7,3,2);
document.write(b);
// Будет выдано: 2,3,5,7,9 

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

function M()
{ s(); return 5; }
 
function N()
{ s(); return 7; }
 
M();   N(); 

Результат исполнения:

function M() { s(); return 5; }
function N() { s(); return 7; } 

Еще одним свойством объекта класса Function является prototype. Но это — общее свойство всех объектов, не только функций, поэтому и обсуждать его мы будем в следующем разделе в контексте типа данных Object. Упомянем только о конструкторе объекта класса Function:

f = new Function(arg_1,...,arg_n, body)

Здесь f — это объект класса Function (его можно использовать как обычную функцию), arg_1, …, arg_n — аргументы функции f, а body — строка, задающая тело функции f.

Данный конструктор можно использовать, например, для описания функций, которые назначают или переопределяют методы объектов. Здесь мы вплотную подошли к вопросу конструирования объектов. Дело в том, что переменные внутри функции можно рассматривать в качестве ее свойств, а функции — в качестве методов:

function Rectangle(a,b,c,d)
{
this.x0 = a;
this.y0 = b;
this.x1 = c;
this.y1 = d;
 
this.area = new Function(
 "return Math.abs((this.x1-this.x0)*(this.y1-this.y0))");
}
 
r = new Rectangle(0,0,30,50);
 
document.write("Площадь: "+r.area());
 
// Будет выведено:
// Площадь: 1500 

Обратите внимание еще на одну особенность — ключевое слово this. Оно позволяет сослаться на текущий объект, в рамках которого происходит исполнение JavaScript-кода. В данном случае это объект класса Rectangle.