Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ASCII и latin.docx
Скачиваний:
3
Добавлен:
08.08.2019
Размер:
190.09 Кб
Скачать

3.4.1. Функциональные литералы

В предыдущем разделе мы видели определение функции square(). С помощью этого синтаксиса обычно описывается большинство функций в JavaScriptпрограммах. Однако стандарт ECMAScript v3 предоставляет синтаксис (реализованный в JavaScript 1.2 и более поздних версиях) для определения функциональных литералов. Функциональный литерал задается с помощью ключевого слова function, за которым следуют необязательное имя функции, список аргументов функции, заключенный в круглые скобки, и тело функции в фигурных скобках. Другими словами, функциональный литерал выглядит так же, как определение функции, правда, у него может не быть имени. Самое большое различие состоит в том, что функциональные литералы могут входить в другие JavaScriptвыражения. То есть функцию square() не обязательно задавать в виде определения:

function square(x) { return x*x; }

Ее можно задать с помощью функционального литерала:

var square = function(x) { return x*x; }

Функции, определенные таким образом, иногда называют лямбдафункциями. Это дань уважения языку программирования LISP, который одним из первых допускал вставку неименованных функций в виде литералов внутрь программы. Хотя в данный момент польза от функциональных литералов может быть неочевидной, позднее, в сложных сценариях мы увидим, что они бывают довольно удобными и полезными. Имеется еще один способ определения функции: можно передать список аргументов и тело функции в виде строк в конструктор Function(). Например:

var square = new Function("x", "return x*x;");

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

6.14. Инструкция function

Инструкция function в JavaScript определяет функцию. Она имеет следующий синтаксис:

function имя_функции([арг1 [,арг2 [..., аргn]]]) {

инструкции

}

Здесь имя_функции – это имя определяемой функции. Оно должно быть идентификатором, а не строкой или выражением. За именем функции следует заключенный в скобки список имен аргументов, разделенных запятыми. Эти идентификаторы могут использоваться в теле функции для ссылки на значения аргументов, переданных при вызове функции. Тело функции состоит из произвольного числа JavaScriptинструкций, заключенных в фигурные скобки. Эти инструкции не исполняются при определении функции. Они компилируются и связываются с новым объектом функции для исполнения при ее вызове с помощью оператора вызова (). Обратите внимание, что фигурные скобки – это обязательная часть инструкции function. В отличие от блоков инструкций в циклах while и других конструкциях, тело функции требует фигурных скобок, даже если оно состоит только из одной инструкции. Определение функции создает новый объект функции и сохраняет объект в только что созданном свойстве с именем имя_функции. Вот несколько примеров определений функций:

function welcome() { alert("Добро пожаловать на мою домашнюю страницу!"); }

function print(msg) {

document.write(msg, "<br>");

}

function hypotenuse(x, y) {

return Math.sqrt(x*x + y*y); // Инструкция return описана далее

}

function factorial(n) { // Рекурсивная функция

if (n <= 1) return 1;

return n * factorial(n 1);

}

Определения функций обычно находятся в JavaScriptкоде верхнего уровня. Они также могут быть вложенными в определения других функций, но только на «верхнем уровне», т. е. определения функции не могут находиться внутри инструкций if, циклов while или любых других конструкций. Формально function не является инструкцией. Инструкции приводят к некоторым динамическим действиям в JavaScriptпрограмме, а определения функций описывают статическую структуру программы. Инструкции исполняются вовремя исполнения программы, а функции определяются во время анализа или компиляции JavaScriptкода, т. е. до их фактического исполнения. Когда синтаксический анализатор JavaScript встречает определение функции, он анализирует и сохраняет (без исполнения) составляющие тело функции инструкции. Затем он определяет свойство (в объекте вызова, если определение функции вложено в другую функцию; в противном случае – в глобальном объекте) с именем, которое было указано в определении функции. Тот факт, что функции определяются на этапе синтаксического анализа, а не во время исполнения, приводит к некоторым интересным эффектам. Рассмотрим следующий фрагмент:

alert(f(4)); // Показывает 16. Функция f() может быть вызвана до того,

// как она определена.

var f = 0; // Эта инструкция переписывает содержимое свойства f.

function f(x) { // Эта "инструкция" определяет функцию f до того,

return x*x; // как будут выполнены приведенные ранее строки.

}

alert(f); // Показывает 0. Функция f() перекрыта переменной f.

Такие необычные результаты возникают изза того, что функция определяется не в то время, в которое определяется переменная. К счастью, подобные ситуации возникают не очень часто. В главе 8 мы узнаем о функциях больше.

6.15. Инструкция return

Как вы помните, вызов функции с помощью оператора () представляет собой выражение. Все выражения имеют значения, и инструкция return служит для определения значения, возвращаемого функцией. Это значение становится значением выражения вызова функции. Инструкция return имеет следующий синтаксис:

return выражение;

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

function square(x) { return x*x; }

Инструкция return может также использоваться без выражения, тогда она просто прерывает исполнение функции, не возвращая значение. Например:

function display_object(obj) {

// Сначала убедимся в корректности нашего аргумента

// В случае некорректности пропускаем остаток функции

if (obj == null) return;6.16. Инструкция throw 115

// Здесь находится оставшаяся часть функции...

}

Если в функции выполняется инструкция return без выражения или если выполнение функции прекращается по причине достижения конца тела функции, значение выражения вызова функции оказывается неопределенным (undefined). JavaScript вставляет точку с запятой автоматически, поэтому нельзя разделять переводом строки инструкцию return и следующее за ней выражение.

8.3. Функции как данные

Самые важные особенности функций заключаются в том, что они могут определяться и вызываться, что было показано в предыдущем разделе. Определение и вызов функций – это синтаксические средства JavaScript и большинства других языков программирования. Однако в JavaScript функции – это не только синтаксические конструкции, но и данные, а это означает, что они могут присваиваться переменным, храниться в свойствах объектов или элементах массивов, передаваться как аргументы функциями и т. д.1 Чтобы понять, как в JavaScript функции могут быть одновременно синтаксическими конструкциями и данными, рассмотрим следующее определение функции:

function square(x) { return x*x; }

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

var a = square(4); // a содержит число 16

var b = square; // b теперь ссылается на ту же функцию, что и square

var c = b(5); // c содержит число 25

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

var o = new Object;

o.square = function(x) { return x*x; }; // функциональный литерал

y = o.square(16); // y равно 256

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

var a = new Array(3);

a[0] = function(x) { return x*x; }

a[1] = 20;

a[2] = a[0](a[1]); // a[2] содержит 400

Синтаксис вызова функции в последнем примере выглядит необычно, однако это вполне допустимый вариант применения оператора () в JavaScript! В примере 8.2 подробно показано, что можно делать, когда функции выступают в качестве данных. Этот пример демонстрирует, каким образом функции могут передаваться другим функциям. Хотя пример может показаться вам несколько сложным, комментарии объясняют, что происходит, и он вполне достоин тщательного изучения.

Пример 8.2. Использование функций как данных

// Здесь определяются несколько простых функций

function add(x,y) { return x + y; }

function subtract(x,y) { return x y; }

function multiply(x,y) { return x * y; }

function divide(x,y) { return x / y; }

// Эта функция принимает одну из вышеприведенных функций

// в качестве аргумента и вызывает ее для двух операндов

function operate(operator, operand1, operand2)

{

return operator(operand1, operand2);

}

// Вот так можно вызвать эту функцию для вычисления значения выражения (2+3) + (4*5):

var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));

// Ради примера, мы реализуем эти функции снова, на этот раз с помощью

// функциональных литералов внутри литерала объекта.

var operators = {

add: function(x,y) { return x+y; },

subtract: function(x,y) { return xy; },

multiply: function(x,y) { return x*y; },

divide: function(x,y) { return x/y; },

pow: Math.pow // Для предопределенных функций это тоже работает

};

// Эта функция принимает имя оператора, отыскивает оператор в объекте,

// а затем вызывает его для переданных операндов. Обратите внимание

// на синтаксис вызова функции оператора.

function operate2(op_name, operand1, operand2)

{

if (typeof operators[op_name] == "function")

return operators[op_name](operand1, operand2);

else throw "неизвестный оператор";

}

// Вот так мы можем вызвать эту функцию для вычисления значения

// ("hello" + " " + "world"):

var j = operate2("add", "hello", operate2("add", " ", "world"))

// Используем предопределенную функцию Math.pow():

var k = operate2("pow", 10, 2)

Если предыдущий пример не убедил вас в удобстве передачи функций в качестве аргументов другим функциям и других способов использования функций как значений, обратите внимание на функцию Array.sort(). Она сортирует элементы массива. Существует много возможных порядков сортировки (числовой, алфавитный, по датам, по возрастанию, по убыванию и т. д.), поэтому функция sort() принимает в качестве необязательного аргумента другую функцию, которая сообщает о том, как выполнять сортировку. Эта функция делает простую работу получает два элемента массива, сравнивает их, а затем возвращает результат, указывающий, какой из элементов должен быть первым. Этот аргумент функции делает метод Array.sort() совершенно универсальным и бесконечно гибким –он может сортировать любой тип данных в любом мыслимом порядке! (Пример использования функции Array.sort() вы найдете в разделе 7.7.3.)

3.5. Объекты

Объект – это коллекция именованных значений, которые обычно называют свойствами (properties) объекта. (Иногда они называются полями объекта, но употребление этого термина может сбить с толку.) Чтобы сослаться на свойство объекта, надо указать имя объекта, затем точку и имя свойства. Например, если объект под названием image имеет свойства width и height, мы можем сослаться на эти свойства следующим образом:

image.width

image.height

Свойства объектов во многом похожи на JavaScriptпеременные – они могут содержать любой тип данных, включая массивы, функции и другие объекты. Поэтому можно встретить вот такой JavaScriptкод:

document.myform.button

Этот фрагмент ссылается на свойство button объекта, который, в свой очередь,хранится в свойстве myform объекта с именем document. Как упоминалось раньше, функция, хранящаяся в свойстве объекта, часто называется методом, а имя свойства становится именем метода. При вызове метода объекта сначала используется оператор «точка» для указания функции, а затем () для вызова этой функции. Например, метод write() объекта с именем document можно вызвать так:

document.write("это проверка");

Объекты в JavaScript могут выступать в качестве ассоциативных массивов, т. е. могут ассоциировать произвольные значения с произвольными строками. При такой работе с объектом обычно требуется другой синтаксис для доступа к его свойствам: строка, содержащая имя требуемого свойства, заключается в квадратные скобки. Тогда к свойствам объекта image, упомянутого ранее, можно обратиться посредством следующего кода:

image["width"]

image["height"]

Ассоциативные массивы – это мощный тип данных; они полезны при реализации ряда технологий программирования. Об объектах, их традиционном применении и применении в качестве ассоциативных массивов рассказано в главе 7.

3.5.1. Создание объектов

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

var o = new Object();

var now = new Date();

var pattern = new RegExp("\\sjava\\s", "i");

Создав собственный объект, можно его как угодно использовать и устанавливать его свойства:

var point = new Object();

point.x = 2.3;

point.y = 1.2;3.6. Массивы 53

3.5.2. Объектные литералы

В JavaScript определяется синтаксис объектных литералов, позволяющий создавать объекты и указывать их свойства. Объектный литерал (также называемый инициализатором объекта) представляет собой список разделенных запятыми пар «свойство/значение», заключенный в фигурные скобки. Внутри пар роль разделителя играет двоеточие. Таким образом, объект point из предыдущего примера также может быть создан и инициализирован следующей строкой:

var point = { x:2.3, y:1.2 };

Объектные литералы могут быть вложенными. Например:

var rectangle = { upperLeft: { x: 2, y: 2 },

lowerRight: { x: 4, y: 4 }

};

Наконец, значениями свойств в объектных литералах не обязательно должны быть константы – это могут быть произвольные JavaScriptвыражения. Кроме того, в качестве имен свойств в объектных литералах допускается использовать строковые значения:

var square = { "upperLeft": { x:point.x, y:point.y },

'lowerRight': { x:(point.x + side), y:(point.y+side) }};

3.5.3. Преобразование объектов

Когда непустой объект используется в логическом контексте, результатом преобразования является значение true. Когда объект используется в строковом контексте, преобразование выполняется методом toString() объекта и в дальнейших вычислениях участвует строка, возвращаемая этим методом. Когда объект используется в числовом контексте, сначала вызывается метод объекта valueOf(). Если этот метод возвращает числовое значение примитивного типа, в дальнейших вычислениях участвует это значение. Однако в большинстве случаев метод valueOf() возвращает сам объект. В такой ситуации объект сначала преобразуется в строку вызовом метода toString(), а затем выполняется попытка преобразовать строку в число.Проблема преобразования объектов в значения примитивных типов имеет свои тонкости, и мы еще вернемся к ней в конце главы.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]