Руководство по javascript, часть 4: функции
Содержание:
Оператор typeof
Оператор возвращает тип аргумента.
У него есть два синтаксиса: со скобками и без:
- Синтаксис оператора: .
- Синтаксис функции: .
Работают они одинаково, но первый синтаксис короче.
Результатом является строка, содержащая тип:
Последние две строки помечены, потому что ведёт себя в них по-особому.
- Результат – это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом деле – это не объект, а отдельный тип данных.
- Функции мы пройдём чуть позже. Пока лишь заметим, что функции не являются отдельным базовым типом в JavaScript, а подвидом объектов. Но выделяет функции отдельно, возвращая для них . На практике это весьма удобно, так как позволяет легко определить функцию.
К работе с типами мы также вернёмся более подробно в будущем, после изучения основных структур данных.
Строка
Строка () в JavaScript должна быть заключена в кавычки.
В JavaScript существует три типа кавычек.
- Двойные кавычки: .
- Одинарные кавычки: .
- Обратные кавычки: .
Двойные или одинарные кавычки являются «простыми», между ними нет разницы в JavaScript.
Обратные кавычки же имеют «расширенную функциональность». Они позволяют нам встраивать выражения в строку, заключая их в . Например:
Выражение внутри вычисляется, и его результат становится частью строки. Мы можем положить туда всё, что угодно: переменную или выражение , или что-то более сложное.
Обратите внимание, что это можно делать только в обратных кавычках. Другие кавычки не имеют такой функциональности встраивания!. Мы рассмотрим строки более подробно в главе Строки
Мы рассмотрим строки более подробно в главе Строки.
Нет отдельного типа данных для одного символа.
В некоторых языках, например C и Java, для хранения одного символа, например или , существует отдельный тип. В языках C и Java это .
В JavaScript подобного типа нет, есть только тип . Строка может содержать один символ или множество.
Объекты обёртки
Каждый раз, когда в программе предпринимается попытка обратиться к свойству или методу значения примитивного типа, интерпретатор временно преобразует примитивное значение в объект соответствующего типа. Временные объекты, в которые преобразуются значения примитивного типа, называют объектами обёртками. Эти объекты используются интерпретатором для доступа к нужному свойству или методу. Сразу после обращения к свойству или методу объект обёртка уничтожается. Объекты обёртки создаются только для значений типа , и . Значения и не имеют объектов оберток: любые попытки обратиться к свойствам этих значений будут вызывать ошибку.
Если для объекта обёртки попытаться установить новое свойство то, новое свойство не будет сохранено, так как объект обёртка уничтожается сразу после того, как выполнит свою работу:
var str = "текст"; str.len = 5; // Установить свойство со значением. Сразу после этого объект уничтожается alert(str.len); // undefined, так как предыдущий объект обёртка уже уничтожен
Объекты обёртки можно рассматривать просто как реализацию удобства для работы со значениями примитивного типа и вообще не думать о них.
С этой темой смотрят:
- Числа
- Infinity и NaN
- Строки
- Boolean
- Преобразование типов данных
Типы данных
Последнее обновление: 26.03.2018
Все используемые данные в javascript имеют определенный тип. В JavaScript имеется пять примитивных типов данных:
-
String: представляет строку
-
Number: представляет числовое значение
-
Boolean: представляет логическое значение или
-
undefined: указывает, что значение не установлено
-
null: указывает на неопределенное значение
Все данные, которые не попадают под вышеперечисленные пять типов, относятся к типу object
Числовые данные
Числа в JavaScript могут иметь две формы:
-
Целые числа, например, 35. Мы можем использовать как положительные, так и отрицательные числа. Диапазон используемых чисел:
от -253 до 253 -
Дробные числа (числа с плавающей точкой), например, 3.5575. Опять же можно использовать как положительные, так и отрицательные числа. Для
чисел с плавающей точкой используется тот же диапазон:
от -253 до 253
Например:
var x = 45; var y = 23.897;
В качестве разделителя между целой и дробной частями, как и в других языках программирования, используется точка.
Строки
Тип представляет строки, то есть такие данные, которые заключены в кавычки. Например, .
Причем мы можем использовать как двойные, так и одинарные кавычки: и . Единственно ограничение:
тип закрывающей кавычки должен быть тот же, что и тип открывающей, то есть либо обе двойные, либо обе одинарные.
Если внутри строки встречаются кавычки, то мы их должны экранировать слешем. Например, пусть у нас есть текст .
Теперь экранируем кавычки:
var companyName = "Бюро \"Рога и копыта\"";
Также мы можем внутри стоки использовать другой тип кавычек:
var companyName1 = "Бюро 'Рога и копыта'"; var companyName2 = 'Бюро "Рога и копыта"';
Тип Boolean
Тип Boolean представляет булевые или логические значения true и false (то есть да или нет):
var isAlive = true; var isDead = false;
null и undefined
Нередко возникает путаница между null и undefined. Итак, когда мы только определяем переменную без присвоения ей начального значения,
она представляет тип undefined:
var isAlive; console.log(isAlive); // выведет undefined
Присвоение значение null означает, что переменная имеет некоторое неопределенное значение (не число, не строка, не логическое значение), но все-таки
имеет значение (undefined означает, что переменная не имеет значения):
var isAlive; console.log(isAlive); // undefined isAlive = null; console.log(isAlive); // null isAlive = undefined; // снова установим тип undefined console.log(isAlive); // undefined
object
Тип представляет сложный объект. Простейшее определение объекта представляют фигурные скобки:
var user = {};
Объект может иметь различные свойства и методы:
var user = {name: "Tom", age:24}; console.log(user.name);
В данном случае объект называется user, и он имеет два свойства: name и age. Это краткое описание объектов, более подробное описание приводится в соответствующей главе.
Слабая типизация
JavaScript является языком со слабой типизацией. Это значит, что переменные могут динамически менять тип. Например:
var xNumber; // тип undefined console.log(xNumber); xNumber = 45; // тип number console.log(xNumber); xNumber = "45"; // тип string console.log(xNumber);
Несмотря на то, что во втором и третьем случае консоль выведет нам число 45, но во втором случае переменная xNumber будет представлять число, а в третьем случае — строку.
Это важный момент, который надо учитывать и от которого зависит поведение переменной в программе:
var xNumber = 45; // тип number var yNumber = xNumber + 5; console.log(yNumber); // 50 xNumber = "45"; // тип string var zNumber = xNumber + 5 console.log(zNumber); // 455
Выше в обоих случая к переменной xNumber применяется операция сложения (+). Но в первом случае xNumber представляет число, поэтому результатом операции
будет число 50.
Во втором случае xNumber представляет строку. Но операция сложения между строкой и числом 5 невозможна. Поэтому число 5 будет преобразовываться к строке,
и будет происходить операция объединения строк. И результатом выражения будет стока «455».
Оператор typeof
С помощью оператора typeof можно получить тип переменной:
var name = "Tom"; console.log(typeof name); // string var income = 45.8; console.log(typeof income); // number var isEnabled = true; console.log(typeof isEnabled); // boolean var undefVariable; console.log(typeof undefVariable); // undefined
НазадВперед
JavaScript Строки
Строка (или текстовая строка) представляет собой последовательность символов, например «John Doe».
Строки пишутся в кавычках. Вы можете использовать одинарные или двойные кавычки:
Пример
var
carName1 = «Volvo XC60»; // Использование двойных кавычек
var
carName2 = ‘Volvo XC60’; // Использование одинарных кавычек
Вы можете использовать кавычки внутри строки, если они не соответствуют кавычкам, окружающим строку:
Пример
var answer1 = «Все нормально»;
// Одинарная кавычка внутри двойных кавычек
var answer2 = «Его зовут ‘Johnny'»;
// Одинарные кавычки внутри двойных кавычек
var answer3 = ‘Его зовут «Johnny»‘;
// Двойные кавычки внутри одинарных кавычек
Вы узнаете больше о строках позже в этом учебнике.
Концепция типов данных
В программировании типы данных являются важной концепцией. Чтобы иметь возможность работать с переменными, важно знать о типе данных
Чтобы иметь возможность работать с переменными, важно знать о типе данных. Без типов данных компьютер не может безопасно решить эту проблему:
Без типов данных компьютер не может безопасно решить эту проблему:
var x = 16 + «Volvo»;
Есть ли смысл добавлять «Volvo» к шестнадцати? Это приведёт к ошибке или покажет результат?
JavaScript будет обрабатывать приведенный выше пример как:
var x =
«16» + «Volvo»;
При добавлении числа и строки JavaScript будет обрабатывать число как строку.
Пример
var x = 16 + «Volvo»;
Пример
var x = «Volvo» + 16;
JavaScript определяет выражения слева направо. Разные последовательности могут давать разные результаты:
JavaScript:
var x = 16 + 4 + «Volvo»;
Результат:
JavaScript:
var x = «Volvo» + 16 + 4;
Результат:
В первом примере JavaScript обрабатывает 16 и 4 как числа, пока не достигнет «Volvo».
Во втором примере, поскольку первый операнд является строкой, все операнды обрабатываются как строки.
Определение функций
Определение функции начинается с ключевого слова function, за которым указываются следующие компоненты:
- Идентификатор, определяющий имя функции
-
Имя является обязательной частью инструкции объявления функции: оно будет использовано для создания новой переменной, которой будет присвоен объект новой функции. В выражениях определения функций имя может отсутствовать: при его наличии имя будет ссылаться на объект функции только в теле самой функции.
- Пара круглых скобок вокруг списка из нуля или более идентификаторов, разделенных запятыми
-
Эти идентификаторы будут определять имена параметров функции и в теле функции могут использоваться как локальные переменные.
- Пара фигурных скобок с нулем или более инструкций JavaScript внутри
-
Эти инструкции составляют тело функции: они выполняются при каждом вызове функции.
В следующем примере показано несколько определений функций в виде инструкций и выражений
Обратите внимание, что определения функций в виде выражений удобно использовать, только если они являются частью более крупных выражений, таких как присваивание или вызов функции, которые выполняют некоторые действия с помощью вновь объявленной функции:. Обратите внимание, что в выражениях определения функций имя функции может отсутствовать
Инструкция объявления функции фактически объявляет переменную и присваивает ей объект функции
Обратите внимание, что в выражениях определения функций имя функции может отсутствовать. Инструкция объявления функции фактически объявляет переменную и присваивает ей объект функции
Выражение определения функции, напротив, не объявляет переменную. Однако в выражениях определения допускается указывать имя функции, как в функции вычисления факториала выше, которое может потребоваться в теле функции для вызова себя самой. Если выражение определения функции включает имя, данное имя будет ссылаться на объект функции в области видимости этой функции. Фактически имя функции становится локальной переменной, доступной только в теле функции. В большинстве случаев имя функции не требуется указывать в выражениях определения, что делает определения более компактными.
Обратите внимание, что большинство (но не все) функций в примере содержат инструкцию return. Инструкция return завершает выполнение функции и выполняет возврат значения своего выражения (если указано) вызывающей программе
Если выражение в инструкции return отсутствует, она возвращает значение undefined. Если инструкция return отсутствует в функции, интерпретатор просто выполнит все инструкции в теле функции и вернет вызывающей программе значение undefined.
Большинство функций в примере вычисляют некоторое значение, и в них инструкция return используется для возврата этого значения вызывающей программе. Функция printprops() несколько отличается в этом смысле: ее работа заключается в том, чтобы вывести имена свойств объекта. Ей не нужно возвращать какое-либо значение, поэтому в функции отсутствует инструкция return. Функция printprops() всегда будет возвращать значение undefined. (Функции, не имеющие возвращаемого значения, иногда называются процедурами.)
Тип данных Undefined
Неопределенный тип образует свой собственный тип, который содержит единственное специальное значение – . Такое значение имеет переменная, объявленная с помощью оператора , но не инициализированная:
Выполнить код »
Скрыть результаты
Значение возвращается при обращении к переменной, которой никогда не присваивалось значение, а также к несуществующему свойству объекта или элементу массива.
Следует отметить, что переменная со значением отличается от переменной, которая вообще не определена:
Выполнить код »
Скрыть результаты
В этом примере метод alert() выводит значение переменной , то есть . Во втором случае в метод alert() передается необъявленная переменная , что приводит к ошибке.
Следующй пример может несколько запутать начинающих программистов, т.к. оператор и для неинициализированной, и для необъявленной переменной возвращает значение :
Выполнить код »
Скрыть результаты
В приведенном примере переменная объявлена, но в неё ничего не записано, поэтому её значение как раз и есть . Переменная не объявлена – её, по сути, нет. Тем не менее, возвращает строку в обоих случаях. Некоторый смысл в этом, конечно, есть, потому что с любой из этих переменных невозможно выполнить никаких операций, хотя технически они совершенно разные.
Примечание: Рекомендуется всегда выполнять инициализацию объявленной пременной. В таком случае вы будете знать, что оператор возвращает из-за того, что переменная не была объявлена, а не потому, что она не инициализирована.
Значение является производным от , так что в ЕСМА-262 оператор эквивалентности считает их равными:
Выполнить код »
Скрыть результаты
Несмотря на то, что значения и связаны, используются они по-разному. Не следует явно присваивать переменной значение , однако к это требование не относится. В случае, когда необходимый объект недоступен, вместо него следует использовать . Это указывает на то, что значение было введено как указатель на пустой объект, и подчеркивает его отличие от .
Чтобы отличать и в программе можно использовать оператор идентичности :
Выполнить код »
Скрыть результаты
Встречная дилемма
Предположим, вы хотите использовать переменную для подсчета чего-то, и вы хотите, чтобы этот счетчик был доступен для всех функций.
Для увеличения счетчика можно использовать глобальную переменную и функцию :
Пример
// Инициировать счетчикvar counter = 0;// Функция для увеличения
счетчикаfunction add() {
counter += 1;
}// Вызвать add() 3 раза
add();
add();
add();// счетчик не должен быть 3
Существует проблема с решением выше: любой код на странице может изменить счетчик, не вызывая add().
Счетчик должен быть локальным для функции , чтобы предотвратить изменение его другим кодом:
Пример
// Инициировать счетчикvar counter = 0;// Функция для увеличения
счетчикаfunction add() { var counter = 0;
counter += 1;}//
Вызвать add() 3 разаadd();add();add();
//Счетчик теперь должен быть 3. Но это 0
Это не сработало, потому что мы показываем глобальный счетчик вместо локального счетчика.
Мы можем удалить глобальный счетчик и получить доступ к локальному счетчику, позволив функции вернуть его:
Пример
// Функция для увеличения счетчикаfunction add() { var counter = 0;
counter += 1; return counter;}// Вызвать add() 3 разаadd();
add();add();//Счетчик теперь должен быть 3. Но это 1.
Это не сработало, потому что мы сбрасываем локальный счетчик каждый раз, когда вызываем функцию.
Внутренняя функция JavaScript может решить эту проблему.
Сравнение с null и undefined
Поведение и при сравнении с другими значениями – особое:
- При строгом равенстве
-
Эти значения различны, так как различны их типы.
- При нестрогом равенстве
-
Эти значения равны друг другу и не равны никаким другим значениям. Это специальное правило языка.
- При использовании математических операторов и других операторов сравнения
-
Значения преобразуются к числам: становится , а – .
Посмотрим, какие забавные вещи случаются, когда мы применяем эти правила
И, что более важно, как избежать ошибок при их использовании
Сравним с нулём:
С точки зрения математики это странно. Результат последнего сравнения говорит о том, что » больше или равно нулю», тогда результат одного из сравнений выше должен быть , но они оба ложны.
Причина в том, что нестрогое равенство и сравнения работают по-разному. Сравнения преобразуют в число, рассматривая его как . Поэтому выражение (3) истинно, а ложно.
С другой стороны, для нестрогого равенства значений и действует особое правило: эти значения ни к чему не приводятся, они равны друг другу и не равны ничему другому. Поэтому (2) ложно.
Значение несравнимо с другими значениями:
Почему же сравнение с нулём всегда ложно?
На это есть следующие причины:
- Сравнения и возвращают , потому что преобразуется в , а – это специальное числовое значение, которое возвращает при любых сравнениях.
- Нестрогое равенство возвращает , потому что равно только и ничему больше.
Зачем мы рассмотрели все эти примеры? Должны ли мы постоянно помнить обо всех этих особенностях? Не обязательно. Со временем все они станут вам знакомы, но можно избежать проблем, если следовать простому правилу.
Просто относитесь к любому сравнению с , кроме строгого равенства , с осторожностью. Не используйте сравнения с переменными, которые могут принимать значения , если вы не уверены в том, что делаете
Если переменная может принимать эти значения, то добавьте для них отдельные проверки
Не используйте сравнения с переменными, которые могут принимать значения , если вы не уверены в том, что делаете. Если переменная может принимать эти значения, то добавьте для них отдельные проверки.
Числа
В отличие от многих языков программирования, в JavaScript не делается различий между целыми и вещественными значениями. Все числа в JavaScript представляются вещественными значениями (с плавающей точкой). Для представления чисел в JavaScript используется 64-битный формат, определяемый стандартом IEEE 754. Этот формат способен представлять числа в диапазоне от ±1,8 x 10308 до ±5 x 10-324.
В JavaScript целые десятичные числа записываются как последовательность цифр. Помимо десятичных целых литералов JavaScript распознает шестнадцатеричные значения. Шестнадцатеричные литералы начинаются с последовательности символов «0x», за которой следует строка шестнадцатеричных цифр. Шестнадцатеричная цифра — это одна из цифр от 0 до 9 или букв от A до F, представляющих значения от 10 до 15:
Литералы вещественных чисел должны иметь десятичную точку — при определении таких литералов используется традиционный синтаксис вещественных чисел. Вещественное значение представляется как целая часть числа, за которой следуют десятичная точка и дробная часть числа.
Литералы вещественных чисел могут также представляться в экспоненциальной нотации: вещественное число, за которым следует буква e (или E), а затем необязательный знак плюс или минус и целая экспонента. Такая форма записи обозначает вещественное число, умноженное на 10 в степени, определяемой значением экспоненты:
Арифметические операции
Обработка чисел в языке JavaScript выполняется с помощью арифметических операторов. В число таких операторов входят: оператор сложения +, оператор вычитания — , оператор умножения *, оператор деления / и оператор деления по модулю % (возвращает остаток от деления).
Помимо этих простых арифметических операторов JavaScript поддерживает более сложные математические операции, с помощью функций и констант, доступных в виде свойств объекта Math:
Арифметические операции в JavaScript не возбуждают ошибку в случае переполнения, потери значащих разрядов или деления на ноль. Если результат арифметической операции окажется больше самого большого представимого значения (переполнение), возвращается специальное значение «бесконечность», которое в JavaScript обозначается как Infinity. Аналогично, если абсолютное значение отрицательного результата окажется больше самого большого представимого значения, возвращается значение «отрицательная бесконечность», которое обозначается как -Infinity.
Эти специальные значения, обозначающие бесконечность, ведут себя именно так, как и следовало ожидать: сложение, вычитание, умножение или деление бесконечности на любое значение дают в результате бесконечность (возможно, с обратным знаком).
Потеря значащих разрядов происходит, когда результат арифметической операции оказывается ближе к нулю, чем минимально возможное значение. В этом случае возвращается число 0. Если потеря значащих разрядов происходит в отрицательном результате, возвращается специальное значение, известное как «отрицательный ноль». Это специальное значение практически ничем не отличается от обычного нуля, и у программистов на JavaScript редко возникает необходимость выделять его.
Деление на ноль не считается ошибкой в JavaScript: в этом случае просто возвращается бесконечность или отрицательная бесконечность. Однако есть одно исключение: операция деления нуля на ноль не имеет четко определенного значения, поэтому в качестве результата такой операции возвращается специальное значение «не число» (not-a-number), которое обозначается как NaN. Значение NaN возвращается также при попытке разделить бесконечность на бесконечность, извлечь квадратный корень из отрицательного числа или выполнить арифметическую операцию с нечисловыми операндами, которые не могут быть преобразованы в числа.
В JavaScript имеются предопределенные глобальные переменные Infinity и NaN, хранящие значения положительной бесконечности и «не число». В стандарте ECMAScript 3 эти переменные доступны для чтения/записи и могут изменяться в программах. Стандарт ECMAScript 5 исправляет эту оплошность и требует, чтобы эти переменные были доступны только для чтения.
Тип объединение¶
Иногда Вы будете работать с функцией, ожидающей параметр типа или .
Например, возьмём следующую функцию:
Проблема с функцией в том, что тип параметра взят как .
Это означает, что мы можем вызвать его с параметрами, не являющимися и , на что TypeScript не выведет никаких ошибок.
В традиционном объектно-ориентированном коде мы могли бы абстрагироваться над двумя типами созданием иерархии типов.
Несмотря на то, что это кажется гораздо более явным, это также немного выходит за рамки дозволенного.
Одним из хороших черт оригинальной версии была возможность передавать только примитивы.
Это значит, что использование было простым и не слишком многословным.
Новый подход также не помог бы при попытке использовать функцию, которая уже существует где-то.
Вместо мы можем использовать тип объединение для параметра :
Тип объединение описывает значение, которое может быть одним из нескольких типов.
Мы используем вертикальную черту (), чтобы отделить каждый тип, так, — это тип значения, который может быть числом , строкой или булевым .
Если у нас есть значение типа объединения, мы можем получить доступ только к тем его элементам, которые являются общими для всех типов в объединении.
Тип объединение может быть немного запутан, потребуется немного интуиции, чтобы привыкнуть.
Если значение имеет тип , мы знаем только то, что оно имеет только те элементы, которые имеют оба и .
В этом примере имеет элемент именованный .
Мы не можем быть уверены в том, есть ли у переменной, имеющей тип , метод .
Если переменная во время выполнения на самом деле является , тогда вызов не выполнится.