I was just wondering, does a function without a return statement (or without hitting any return statements) return a value that is completely equivalent to false?
For example:
function foo(){};
!!foo();
This should return false if executed in firebug (but does not return anything if i just called foo();).
Thanks a lot!
Jason
asked Oct 13, 2009 at 1:00
FurtiveFelonFurtiveFelon
14.7k27 gold badges76 silver badges97 bronze badges
1
A function without a return statement (or one that ends its execution without hitting one) will return undefined
.
And if you use the unary negation operator twice on an undefined
value, you will get false
.
You are not seeing anything on the console because Firebug doesn’t prints the result of an expression when it’s undefined (just try typing undefined;
at the console, and you will see nothing).
However if you call the console.log
function directly, and you will be able to see it:
function foo(){}
console.log(foo()); // will show 'undefined'
answered Oct 13, 2009 at 1:02
0
<html>
<body>
<script>
function a() {}
alert(a());
</script>
</body>
</html>
answered Oct 13, 2009 at 1:26
to find out, try this in firebug console:
alert((function(){})());
answered Oct 13, 2009 at 1:38
Scott EverndenScott Evernden
39.2k15 gold badges79 silver badges84 bronze badges
Автор вопроса: Авангардов Шапошников
Опубликовано: 02/04/2023
У нас есть 24 ответов на вопрос Какое значение возвращает функция в теле которой отсутствует инструкция return? Скорее всего, этого будет достаточно, чтобы вы получили ответ на ваш вопрос.
- Что вернет функция без return JS?
- Что возвращает функция если в ней не указано ключевое слово return?
- Что возвращает функция без return Python?
- Какое значение возвращает функция в теле которой отсутствует инструкция return JS?
- Что делает пустой return?
- Какое значение возвращает функция?
- Что возвращает return Python?
- Для чего нужна команда return в Питоне?
- Что значит возвращает значение?
- Какое значение возвращает функция в теле которой отсутствует инструкция return? Ответы пользователей
- Какое значение возвращает функция в теле которой отсутствует инструкция return? Видео-ответы
Отвечает Дмитрий Рыжиков
Если выражение в инструкции return отсутствует, она возвращает значение undefined. Если инструкция return отсутствует в функции, интерпретатор просто выполнит все инструкции в теле функции и вернет вызывающей программе значение undefined.
Что вернет функция без return JS?
Чтобы вернуть значение, отличное от значения по умолчанию, в функции должна быть инструкция return , которая указывает, что именно нужно вернуть. Функция без него вернёт значение по умолчанию.
Что возвращает функция если в ней не указано ключевое слово return?
Если в определении функции оператор return не указан, то после выполнения последнего оператора вызванной функции управление автоматически возвращается вызывающей функции. В этом случае возвращаемое значение вызванной функции не определено.
Что возвращает функция без return Python?
Return возвращает значение функции, возвращать означает выдать результат вычисления функции. Без return функция ничего не возвращает.
Какое значение возвращает функция в теле которой отсутствует инструкция return JS?
Если выражение в инструкции return отсутствует, она возвращает значение undefined. Если инструкция return отсутствует в функции, интерпретатор просто выполнит все инструкции в теле функции и вернет вызывающей программе значение undefined.
Что делает пустой return?
Оператор return используется для остановки выполнения функции и возвращения значения expression. Если аргумент expression опущен или в функции не выполняется оператор return, то выражению, вызвавшему текущую функцию, присваивается значение undefined. То value следуя этой логике примет значение undefined.
Какое значение возвращает функция?
Возврат значений подпрограммой То, что функция «возвращает значение», фактически означает, что результат её вызова может быть использован: для присваивания переменной или как часть других выражений.
Что возвращает return Python?
Оператор return используется в функциях для возвращения данных после выполнения работы самой функции. В примере выше вызывается функция, которая считает сумму трех переданных аргументов. В конце функция возвращает это значение, поэтому мы можем записать функцию, как присвоение данных к переменной.
Для чего нужна команда return в Питоне?
Функция в python — объект, принимающий аргументы и возвращающий значение. Обычно функция определяется с помощью инструкции def. Инструкция return говорит, что нужно вернуть значение. В нашем случае функция возвращает сумму x и y.
Что значит возвращает значение?
Возврат значений подпрограммой То, что функция «возвращает значение», фактически означает, что результат её вызова может быть использован: для присваивания переменной или как часть других выражений.
Отвечает Адель Албогачиев
Оператор return завершает выполнение текущей функции и возвращает её значение. … выражение: Выражение, значение которого будет возвращено.
Отвечает Олег Болдырев
Функция всегда возвращает значение, а процедура может возвращать, … от значения по умолчанию, в функции должна быть инструкция return …
Отвечает Кирилл Мурашко
function sum(a, b) { return a + b; } let result = sum(1, 2); alert( result ); // 3 … Если функция не возвращает значения, это всё равно, …
Отвечает Кристина Салахиева
В языке C оператор return завершает выполнение функции и при необходимости возвращает значение в вызывающую функцию.
Отвечает Вадим Лехнер
С помощью return возвращают значение из тела функции в основную программу. В Python можно вернуть несколько значений, перечислив их через запятую после …
Отвечает Виталий Калашников
Для завершения выполнения функции и, если нужно, возврата значения, в ее теле используется предложение return. Когда интерпретатор во время выполнения функции …
Отвечает Алина Кивилева
Функция, в которой находится вызов, называется caller, а функция, … Эта функция не возвращает никакого значения, поэтому оператор return здесь не нужен.
Отвечает Ян Шишкарёв
По умолчанию она возвращает значение undefined . Для явного указания значения, которое должна возвращать функция используется инструкция return …
Отвечает Михаил Васильев
func f(x, y) { # x имеет значение 1, y имеет значение 2 return x + y } f(1, … Если в теле функции нет инструкции return , то после последнего выражения в …
«Функция возвращает» Что это значит? JavaScript
Благодарю за просмотр! Вы можете поддержать канал монеткой — Номер карты — 4276 5500 2378 3189 Ваша …
37 Возвращаемое значение функции. Оператор return Python
Если в функции отсутствует оператор return, функция по умолчанию возвращает значение None.
Return в функции — учимся возвращать значение функции в JavaScript. Функции в JavaScript
Оператор return в JavaScript. Прерывание функции с помощью return. Return и перенос строки. Возвращаем функции с …
Функция возвращает Функцию в JavaScript
00:00 Вступление. 00:45 Функция возвращает Что это значит? — Оператор return. Возвращаем примитивы. 02:30 …
Функции. Return — это просто!
https://js2.itgid.info React курс: https://itgid.info/react Этот урок:https://js2.itgid.info/unit?unit=function VK: …
Что такое return в C++?
258
Последнее обновление: 2022-02-26 12:02:00
Оператор return завершает выполнение функции и возвращает управление вызывающей функции. Выполнение возобновляется в вызывающей функции в точке сразу после вызова.17 дек. 2021 г.
Оператор return завершает выполнение текущей функции и возвращает её значение.29 окт. 2021 г.
Какое значение возвращает функция?
То, что функция «возвращает значение», фактически означает, что результат её вызова может быть использован: для присваивания переменной или как часть других выражений.10 апр. 2017 г.
Какое значение возвращает функция в теле которой отсутствует инструкция return?
Если выражение в инструкции return отсутствует, она возвращает значение undefined. Если инструкция return отсутствует в функции, интерпретатор просто выполнит все инструкции в теле функции и вернет вызывающей программе значение undefined.
Что такое тело функции?
Тело функции – это программный код (блок), ограниченный фигурными скобками. Тело функции при трансляции преобразуется во внутреннее (двоичное) представление и размещается в сегменте команд программы (см. 1.2). Существует также синтаксис, в котором присутствует только заголовок, ограниченный символом «;».
Что такое вызов функции?
Вызов функции – это выражение, значением которого является значение, возвращаемое функцией. Описанный тип функции должен соответствовать типу возвращаемого значения. Функция может быть объявлена или описана в любом месте программы на глобальном уровне, то есть, вне других функций.
Мне просто интересно, функция без оператора return (или без нажатия каких-либо операторов return) возвращает значение, полностью эквивалентное false?
например:
function foo(){};
!!foo();
Это должно вернуть false, если выполняется в firebug (но ничего не возвращает, если я просто вызвал foo ();).
Спасибо большое!
Джейсон
3 ответов
функция без оператора return (или та, которая завершает ее выполнение, не нажимая один) будет возвращать undefined
.
и если вы используете унарный оператор отрицания дважды на undefined
значением, вы получите false
.
вы ничего не видите на консоли, потому что Firebug не печатает результат выражения, когда он не определен (просто попробуйте ввести undefined;
в консоли, и вы ничего не увидите).
однако если вы вызываете console.log
функция сразу, и вы будете увидеть его:
function foo(){}
console.log(foo()); // will show 'undefined'
<html>
<body>
<script>
function a() {}
alert(a());
</script>
</body>
</html>
чтобы узнать, попробуйте это в консоли firebug:
alert((function(){})());
Я продолжаю серию публикаций адаптированного и дополненного перевода "Карманной книги по TypeScript
«.
Другие части:
- Часть 1. Основы
- Часть 2. Типы на каждый день
- Часть 3. Сужение типов
- Часть 4. Подробнее о функциях
- Часть 5. Объектные типы
- Часть 6. Манипуляции с типами
- Часть 7. Классы
- Часть 8. Модули
Обратите внимание: для большого удобства в изучении книга была оформлена в виде прогрессивного веб-приложения.
Функции — это основные строительные блоки любого приложения, будь то функции, импортируемые из другого модуля, или методы класса. В TS
существует несколько способов описания того, как фукнции вызываются.
Тип функции в форме выражения (function type expressions)
Простейшим способом описания типа функции является выражение. Такие типы похожи на стрелочные функции:
function greeter(fn: (a: string) => void) {
fn('Hello, World')
}
function printToConsole(s: string) {
console.log(s)
}
greeter(printToConsole)
Выражение (a: string) => void
означает «функция с одним параметром a
типа string
, которая ничего не возвращает». Как и в случае с определением функции, если тип параметра не указан, он будет иметь значение any
.
Обратите внимание: название параметра является обязательным. Тип функции (string) => void
означает «функция с параметром string
типа any
«!
Разумеется, для типа функции можно использовать синоним:
type GreetFn = (a: string) => void
function greeter(fn: GreetFn) {
// ...
}
Сигнатуры вызова (call signatures)
В JS
функции, кроме того, что являются вызываемыми (callable), могут иметь свойства. Однако, тип-выражение не позволяет определять свойства функции. Для описания вызываемой сущности (entity), обладающей некоторыми свойствами, можно использовать сигнатуру вызова (call signature) в объектном типе:
type DescFn = {
description: string
(someArg: number): boolean
}
function doSomething(fn: DescFn) {
console.log(`Значением, возвращаемым ${fn.description} является ${fn(6)}`)
}
Обратите внимание: данный синтаксис немного отличается от типа-выражения функции — между параметрами и возвращаемым значением используется :
вместо =>
.
Сигнатуры конструктора (construct signatures)
Как известно, функции могут вызываться с ключевым словом new
. TS
считает такие функции конструкторами, поскольку они, как правило, используются для создания объектов. Для определения типов таких функций используется сигнатура конструктора:
type SomeConstructor = {
new (s: string): SomeObject
}
function fn(ctor: SomeConstructor) {
return new ctor('Hello!')
}
Некоторые объекты, такие, например, как объект Date
, могут вызываться как с, так и без new
. Сигнатуры вызова и конструктора можно использовать совместно:
interface CallOrConstruct {
new (s: string): Date
(n?: number): number
}
Общие функции или функции-дженерики (generic functions)
Часто тип данных, возвращаемых функцией, зависит от типа передаваемого функции аргумента или же два типа возвращаемых функцией значений зависят друг от друга. Рассмотрим функцию, возвращающую первый элемент массива:
function firstElement(arr: any[]) {
return arr[0]
}
Функция делают свою работу, но, к сожалению, типом возвращаемого значения является any
. Было бы лучше, если бы функция возвращала тип элемента массива.
В TS
общие типы или дженерики (generics) используются для описания связи между двумя значениями. Это делается с помощью определения параметра Type
в сигнатуре функции:
function firstElement<Type>(arr: Type[]): Type {
return arr[0]
}
Добавив параметр Type
и использовав его в двух местах, мы создали связь между входящими данными функции (массивом) и ее выходными данными (возвращаемым значением). Теперь при вызове функции возвращается более конкретный тип:
// `s` имеет тип `string`
const s = firstElement(['a', 'b', 'c'])
// `n` имеет тип `number`
const n = firstElement([1, 2, 3])
Предположение типа (inference)
Мы можем использовать несколько параметров типа. Например, самописная версия функции map
может выглядеть так:
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
return arr.map(func)
}
// Типом `n` является `string`,
// а типом `parsed` - `number[]`
const parsed = map(['1', '2', '3'], (n) => parseInt(n))
Обратите внимание, что в приведенном примере TS
может сделать вывод относительно типа Input
на основе переданного string[]
, а относительно типа Output
на основе возвращаемого number
.
Ограничения (constraints)
Ограничение, как следует из названия, используется для ограничения типов, принимаемых параметром типа.
Реализуем функцию, возвращающую самое длинное из двух значений. Для этого нам потребуется свойство length
, которое будет числом. Мы ограничим параметр типа типом number
с помощью ключевого слова extends
:
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a
} else {
return b
}
}
// Типом `longerArr` является `number[]`
const longerArr = longest([1, 2], [1, 2, 3])
// Типом `longerStr` является `string`
const longerStr = longest('alice', 'bob')
// Ошибка! У чисел нет свойства `length`
const notOK = longest(10, 100)
// Argument of type 'number' is not assignable to parameter of type '{ length: number }'.
// Аргумент типа 'number' не может быть присвоен параметру типа '{ length: number; }'
Мы позволяем TS
предполагать тип значения, возвращаемого из функции longest
.
Поскольку мы свели Type
к { length: number }
, то получили доступ к свойству length
параметров a
и b
. Без ограничения типа у нас бы не было такого доступа, потому что значения этих свойств могли бы иметь другой тип — без длины.
Типы longerArr
и longerStr
были выведены на основе аргументов. Запомните, дженерики определяют связь между двумя и более значениями одного типа!
Наконец, как мы и ожидали, вызов longest(10, 100)
отклоняется, поскольку тип number
не имеет свойства length
.
Работа с ограниченными значениями
Вот пример распространенной ошибки, возникающей при работе с ограничениями дженериков:
function minLength<Type extends { length: number }>(
obj: Type,
min: number
): Type {
if (obj.length >= min) {
return obj
} else {
return { length: min }
}
}
// Type '{ length: number; }' is not assignable to type 'Type'. '{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
// Тип '{ length: number; }' не может быть присвоен типу 'Type'. '{ length: number; }' может присваиваться ограничению типа 'Type', но 'Type' может быть инстанцирован с другим подтипом ограничения '{ length: number; }'
На первый взгляд может показаться, что все в порядке — Type
сведен к { length: number }
, и функция возвращает либо Type
, либо значение, совпадающее с ограничением. Проблема состоит в том, что функция может вернуть объект, идентичный тому, который ей передается, а не просто объект, совпадающий с ограничением. Если бы во время компиляции не возникло ошибки, мы могли бы написать что-то вроде этого:
// `arr` получает значение `{ length: 6 }`
const arr = minLength([1, 2, 3], 6)
// и ломает приложение, поскольку массивы
// имеют метод `slice`, но не возвращаемый объект!
console.log(arr.slice(0))
Определение типа аргументов
Обычно, TS
делает правильные выводы относительно типов аргументов в вызове дженерика, но так бывает не всегда. Допустим, мы реализовали такую функцию для объединения двух массивов:
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2)
}
При обычном вызове данной функции с несовпадающими по типу массивами возникает ошибка:
const arr = combine([1, 2, 3], ['привет'])
// Type 'string' is not assignable to type 'number'.
Однако, мы можем вручную определить Type
, и тогда все будет в порядке:
const arr = combine<string | number>([1, 2, 3], ['привет'])
Руководство по написанию хороших функций-дженериков
Используйте параметры типа без ограничений
Рассмотрим две похожие функции:
function firstElement1<Type>(arr: Type[]) {
return arr[0]
}
function firstElement2<Type extends any[]>(arr: Type) {
return arr[0]
}
// a: number (хорошо)
const a = fisrtElement1([1, 2, 3])
// b: any (плохо)
const b = fisrtElement2([1, 2, 3])
Предполагаемым типом значения, возвращаемого функцией firstElement1
является Type
, а значения, возвращаемого функцией firstElement2
— any
. Это объясняется тем, что TS
разрешает (resolve) выражение arr[0]
с помощью ограничения типа вместо того, чтобы ждать разрешения элемента после вызова функции.
Правило: по-возможности, используйте параметры типа без ограничений.
Используйте минимальное количество параметров типа
Вот еще одна парочка похожих функций:
function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
return arr.filter(func)
}
function filter2<Type, Func extends (arg: Type) => boolean>(
arr: Type[],
func: Func
): Type[] {
return arr.filter(func)
}
Во втором случае мы создаем параметр типа Func
, который не связывает значения. Это означает, что при вызове функции придется определять дополнительный аргумент типа без веских на то причин. Это не есть хорошо.
Правило: всегда используйте минимальное количество параметров типа.
Параметры типа должны указываться дважды
Иногда мы забываем, что функция не обязательно должна быть дженериком:
function greet<Str extends string>(s: Str) {
console.log(`Привет, ${s}!`)
}
greet('народ')
Вот упрощенная версия данной функции:
function greet(s: string) {
console.log(`Привет, ${s}!`)
}
Запомните, параметры типа предназначены для связывания типов нескольких значений.
Правило: если параметр типа появляется в сигнатуре функции только один раз, то, скорее всего, он вам не нужен.
Опциональные параметры (optional parameters)
Функции в JS
могут принимать произвольное количество аргументов. Например, метод toFixed
принимает опциональное количество цифр после запятой:
function fn(n: number) {
console.log(n.toFixed()) // 0 аргументов
console.log(n.toFixed(3)) // 1 аргумент
}
Мы можем смоделировать это в TS
, пометив параметр как опциональный с помощью ?
:
function f(x?: number) {
// ...
}
f() // OK
f(10) // OK
Несмотря на то, что тип параметра определен как number
, параметр x
на самом деле имеет тип number | undefined
, поскольку неопределенные параметры в JS
получают значение undefined
.
Мы также можем указать «дефолтный» параметр (параметр по умолчанию):
function f(x = 10) {
// ...
}
Теперь в теле функции f
параметр x
будет иметь тип number
, поскольку любой аргумент со значением undefined
будет заменен на 10
. Обратите внимание: явная передача undefined
означает «отсутствующий» аргумент.
declare function f(x?: number): void
// OK
f()
f(10)
f(undefined)
Опциональные параметры в функциях обратного вызова
При написании функций, вызывающих «колбеки», легко допустить такую ошибку:
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i)
}
}
Указав index?
, мы хотим, чтобы оба этих вызова были легальными:
myForEach([1, 2, 3], (a) => console.log(a))
myForEach([1, 2, 3], (a, i) => console.log(a, i))
В действительности, это означает, что колбек может быть вызван с одним аргументом. Другими словами, определение функции говорит, что ее реализация может выглядеть так:
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i])
}
}
Поэтому попытка вызова такой функции приводит к ошибке:
myForEach([1, 2, 3], (a, i) => {
console.log(i.toFixed())
// Object is possibly 'undefined'.
// Возможным значением объекта является 'undefined'
})
В JS
при вызове функции с большим (ударение на первый слог) количеством аргументов, чем указано в определении фукнции, дополнительные параметры просто игнорируются. TS
ведет себя аналогичным образом. Функции с меньшим количеством параметров (одного типа) могут заменять функции с большим количеством параметров.
Правило: при написании типа функции для колбека, не указывайте опциональные параметры до тех пор, пока не будете вызывать функцию без передачи этих параметров.
Перегрузка функции (function overload)
Некоторые функции могут вызываться с разным количеством аргументов. Например, мы можем написать функцию, возвращающую Date
, которая принимает время в мс (timestamp, один аргумент) или день/месяц/год (три аргумента).
В TS
такую функцию можно реализовать с помощью сигнатур перегрузки (overload signatures). Для этого перед телом функции указывается несколько ее сигнатур:
function makeDate(timestamp: number): Date
function makeDate(d: number, m: number, y: number): Date
function makeDate(dOrTimestamp: number, m?: number, y?: number): Date {
if (m !== undefined && y !== undefined) {
return new Date(y, m, dOrTimestamp)
} else {
return new Date(dOrTimestamp)
}
}
const d1 = makeDate(12345678)
const d2 = makeDate(5, 5, 5)
const d3 = makeDate(1, 3)
// No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
// Нет перегрузки, принимающей 2 аргумента, но существуют перегрузки, ожидающие получения 1 или 3 аргумента
В приведенном примере мы реализовали две перегрузки: одну, принимающую один аргумент, и вторую, принимающую три аргумента. Первые две сигнатуры называются сигнатурами перегрузки.
Затем мы реализовали функцию с совместимой сигнатурой (compatible signature). Функции имеют сигнатуру реализации (implementation signature), но эта сигнатура не может вызываться напрямую. Несмотря на то, что мы написали функцию с двумя опциональными параметрами после обязательного, она не может вызываться с двумя параметрами!
Сигнатуры перегрузки и сигнатура реализации
Предположим, что у нас имеется такой код:
function fn(x: string): void
function fn() {
// ...
}
// Мы ожидаем, что функция может вызываться без аргументов
fn()
// Expected 1 arguments, but got 0.
// Ожидалось получение 1 аргумента, а получено 0
Почему в данном случае возникает ошибка? Дело в том, что сигнатура реализации не видна снаружи (за пределами тела функции). Поэтому при написании перегруженной функции всегда нужно указывать две или более сигнатуры перегрузки перед сигнатурой реализации.
Кроме того, сигнатура реализации должна быть совместима с сигнатурами перегрузки. Например, при вызове следующих функций возникают ошибки, поскольку сигнатура реализации не совпадает с сигнатурами перегрузки:
function fn(x: boolean): void
// Неправильный тип аргумента
function fn(x: string): void
// This overload signature is not compatible with its implementation signature.
// Данная сигнатура перегрузки не совместима с сигнатурой ее реализации
function(x: boolean) {}
function fn(x: string): string
// Неправильный тип возвращаемого значения
function(x: number): boolean
// This overload signature is not compatible with its implementation signature.
function fn(x: string | number) {
return 'упс'
}
Правила реализации хороших перегрузок функции
Рассмотрим функцию, возвращающую длину строки или массива:
function len(s: string): number
function len(arr: any[]): number
function len(x: any) {
return x.length
}
На первый взгляд кажется, что все в порядке. Мы можем вызывать функцию со строками или массивами. Однако, мы не можем вызывать ее со значением, которое может быть либо строкой, либо массивом, поскольку TS
ассоциирует вызов функции с одной из ее перегрузок:
len('') // OK
len([0]) // OK
len(Math.random() > 0.5 ? 'привет' : [0])
/*
No overload matches this call.
Overload 1 of 2, '(s: string): number', gave the following error.
Argument of type 'number[] | "привет"' is not assignable to parameter of type 'string'.
Type 'number[]' is not assignable to type 'string'.
Overload 2 of 2, '(arr: any[]): number', gave the following error.
Argument of type 'number[] | "привет"' is not assignable to parameter of type 'any[]'.
Type 'string' is not assignable to type 'any[]'.
*/
/*
Ни одна из перегрузок не совпадает с вызовом.
Перегрузка 1 из 2, '(s: string): number', возвращает следующую ошибку.
Аргумент типа 'number[] | "привет"' не может быть присвоен параметру типа 'string'.
Тип 'number[]' не может быть присвоен типу 'string'.
Перегрузка 2 из 2, '(arr: any[]): number', возвращает следующую ошибку.
Аргумент типа 'number[] | "привет"' не может быть присвоен типу 'any[]'.
Тип 'string' не может быть присвоен типу 'any[]'.
*/
Поскольку обе перегрузки имеют одинаковое количество аргументов и один и тот же тип возвращаемого значения, мы можем реализовать такую «неперегруженную» версию данной функции:
function len(x: any[] | string) {
return x.length
}
Так намного лучше! Теперь мы можем вызывать функцию с любым значением и, кроме того, нам не нужно предварительно определять правильную сигнатуру реализации функцию.
Правило: по-возможности используйте объединения вместо перегрузок функции.
Определение this
в функциях
Рассмотрим пример:
const user = {
id: 123,
admin: false,
becomeAdmin: function() {
this.admin = true
}
}
TS
«понимает», что значением this
функции user.becomeAdmin
является внешний объект user
. В большинстве случаев этого достаточно, но порой нам требуется больше контроля над тем, что представляет собой this
. Спецификация JS
определяет, что мы не можем использовать this
в качестве названия параметра. TS
использует это синтаксическое пространство (syntax space), позволяя определять тип this
в теле функции:
const db = getDB()
const admins = db.filterUsers(function() {
return this.admin
})
Обратите внимание: в данном случае мы не можем использовать стрелочную функцию.
const db = getDB()
const admins = db.filterUsers(() => this.admin)
// The containing arrow function captures the global value of 'this'. Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
// Стрелочная функция перехватывает глобальное значение 'this'. Неявным типом элемента является 'any', поскольку тип 'typeof globalThis' не имеет сигнатуры индекса
Другие типы, о которых следует знать
void
void
представляет значение, возвращаемое функцией, которая ничего не возвращает. Если в теле функции отсутствует оператор return
или после этого оператора не указано возвращаемого значения, предполагаемым типом возвращаемого такой функцией значения будет void
:
// Предполагаемым типом является `void`
function noop() {
return
}
В JS
функция, которая ничего не возвращает, «неявно» возвращает undefined
. Однако, в TS
void
и undefined
— это разные вещи.
Обратите внимание: void
— это не тоже самое, что undefined
.
object
Специальный тип object
представляет значение, которое не является примитивом (string, number, boolean, symbol, null, undefined
). object
отличается от типа пустого объекта ({}
), а также от глобального типа Object
. Скорее всего, вам никогда не потребуется использовать Object
.
Правило: object
— это не Object
. Всегда используйте object
!
Обратите внимание: в JS
функции — это объекты: они имеют свойства, Object.prototype
в цепочке прототипов, являются instanceof Object
, мы можем вызывать на них Object.keys
и т.д. По этой причине в TS
типом функций является object
.
unknown
Тип unknown
представляет любое значение. Он похож на тип any
, но является более безопасным, поскольку не позволяет ничего делать с неизвестным значением:
function f1(a: any) {
a.b() // OK
}
function f2(a: unknown) {
a.b()
// Object is of type 'unknown'.
// Типом объекта является 'unknown'
}
Это бывает полезным для описания типа функции, поскольку таким способом мы можем описать функцию, принимающую любое значение без использования типа any
в теле функции. Другими словами, мы можем описать функцию, возвращающую значение неизвестного типа:
function safeParse(s: string): unknown {
return JSON.parse(s)
}
const obj = safeParse(someRandomString)
never
Некоторые функции никогда не возвращают значений:
function fail(msg: string): never {
throw new Error(msg)
}
Тип never
представляет значение, которого не существует. Чаще всего, это означает, что функция выбрасывает исключение или останавливает выполнение программы.
never
также появляется, когда TS
определяет, что в объединении больше ничего не осталось:
function fn(x: string | number) {
if (typeof x === 'string') {
// ...
} else if (typeof x === 'number') {
// ...
} else {
x // типом `x` является `never`!
}
}
Function
Глобальный тип Function
описывает такие свойства как bind
, call
, apply
и другие, характерные для функций в JS
. Он также имеет специальное свойство, позволяющее вызывать значения типа Function
— такие вызовы возвращают any
:
function doSomething(f: Function) {
f(1, 2, 3)
}
Такой вызов функции называется нетипизированным и его лучше избегать из-за небезопасного возвращаемого типа any
.
Если имеется необходимость принимать произвольную функцию без ее последующего вызова, лучше предпочесть более безопасный тип () => void
.
Оставшиеся параметры и аргументы
Оставшиеся параметры (rest parameters)
Кроме использования опциональных параметров или перегрузок для создания функций, принимающих разное или фиксированное количество аргументов, мы можем определять функции, принимающие произвольное количество аргументов с помощью синтаксиса оставшихся параметров.
Оставшиеся параметры указываются после других параметров с помощью ...
:
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x)
}
// `a` получает значение [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4)
В TS
неявным типом таких параметров является any[]
, а не any
. Любая аннотация типа для них должна иметь вид Array<T>
или T[]
, или являться кортежем.
Оставшиеся аргументы (rest arguments)
Синтаксис распространения (синонимы: расширение, распаковка) (spread syntax) позволяет передавать произвольное количество элементов массива. Например, метод массива push
принимает любое количество аргументов:
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
arr1.push(...arr2)
Обратите внимание: TS
не считает массивы иммутабельными. Это может привести к неожиданному поведению:
// Предполагаемым типом `args` является `number[]` - массив с 0 или более чисел
// а не конкретно с 2 числами
const args = [8, 5]
const angle = Math.atan2(...args)
// Expected 2 arguments, but got 0 or more.
// Ожидалось получение 2 аргументов, а получено 0 или более
Самым простым решением данной проблемы является использование const
:
// Предполагаемым типом является кортеж, состоящий из 2 элементов
const args = [8, 5] as const
// OK
const angle = Math.atan2(...args)
Деструктуризация параметров (parameter destructuring)
Деструктуризация параметров используется для распаковки объекта, переданного в качестве аргумента, в одну или более локальную переменную в теле функции. В JS
это выглядит так:
function sum({ a, b, c }) {
console.log(a + b + c)
}
sum({ a: 10, b: 3, c: 9 })
Аннотация типа для объекта указывается после деструктуризации:
function sum({ a, b, c }: { a: number, b: number, c: number }) {
console.log(a + b + c)
}
Для краткости можно использовать именованный тип:
type ABC = { a: number, b: number, c: number }
function sum({ a, b, c }: ABC) {
console.log(a + b + c)
}
Возможность присвоения функций переменным
Использование void
в качестве типа возвращаемого функцией значения может приводить к необычному, но вполне ожидаемому поведению.
Контекстуальная типизация (contextual typing), основанная на void
, не запрещает функции что-либо возвращать. Другими словами, функция, типом возвращаемого значения которой является void
— type vf = () => void
, может возвращать любое значение, но это значение будет игнорироваться.
Все приведенные ниже реализации типа () => void
являются валидными:
type voidFn = () => void
const f1: voidFn = () => {
return true
}
const f2: voidFn = () => true
const f3: voidFn = function() {
return true
}
Когда возвращаемое любой из этих функций значение присваивается переменной, она будет хранить тип void
:
const v1 = f1()
const v2 = f2()
const v3 = f3()
Поэтому следующий код является валидным, несмотря на то, что Array.prototype.push
возвращает число, а Array.prototype.forEach
ожидает получить функцию с типом возвращаемого значения void
:
const src = [1, 2, 3]
const dst = [0]
src.forEach((el) => dist.push(el))
Существует один специальный случай, о котором следует помнить: когда литеральное определение функции имеет тип возвращаемого значения void
, функция не должна ничего возвращать:
function f2(): void {
// Ошибка
return true
}
const f3 = function(): void {
// Ошибка
return true
}
Облачные серверы от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!
Функции помогают снизить повторяемость кода за счет превращения часто встречающихся участков кода в независимые блоки, которые определяются один раз и могут вызываться многократно. В этой главе описаны основы их
применения и соответствующие механизмы.
JavaScript Функции
Функция (function) – это самостоятельный фрагмент кода, решающий определенную задачу. Каждой функции присваивается уникальное имя, по которому ее можно идентифицировать и «вызвать» в нужный момент.
Функции в языке JavaScript являются объектами, и следовательно ими можно манипулировать как объектами. Например, функции могут присваиваться переменным, элементам массива, свойствам объектов, передавать в качестве аргумента функциям и возвращать в качестве значения из функций.
В JavaScript есть встроенные функции, которые можно использовать в программах, но код которых нельзя редактировать или посмотреть.
Примеры встроенных функций вы уже видели – это alert()
, prompt()
, confirm()
и document.write()
. Кроме использования встроенных функций, вы можете создать свои собственные, так называемые пользовательские функции.
Объявление и вызов функции
Функция (как и всякий объект) должна быть объявлена (определена) перед её использованием.
Функция JavaScript определяется с помощью ключевого слова function
, за которым указываются следующие компоненты:
- Идентификатор, определяющий имя функции. Чаще всего в качестве имен функций выбираются глаголы или фразы, начинающиеся с глаголов. По общепринятому соглашению имена функций начинаются со строчной буквы.
- Пара круглых скобок
()
, которые могут включать имена параметров, разделенных запятыми ( параметр1, параметр2, … ). Эти имена параметров в теле функции могут использоваться как локальные переменные. - Пара фигурных скобок
{}
с инструкциями JavaScript внутри. Эти инструкции составляют тело функции: они выполняются при каждом вызове функции.
Синтаксис функции JavaScript:
function идентификатор(параметры) {
инструкции
}
Программный код, расположенный в теле функции, выполняется не в момент объявления функции, а в момент её вызова. Будучи один раз определенной, функция может сколько угодно раз вызываться с помощью оператора ()
(вызов функции):
Этот код выведет нам приветствие в диалоговом окне alert()
два раза. Применение функции позволило нам избавиться от дублирования кода.
Обратите внимание, что несмотря на отсутствие параметров, в объявлении функции все равно нужно ставить скобки ()
после имени. При вызове, после имени функции, также указываются пустые скобки ()
.
Интерпретатор JavaScript, встречая ключевое слово function
, создаёт функцию и переменную с именем этой функции. Переменной присваивается ссылка на функцию.
Обратившись к переменной с именем созданной функции можно увидеть, что в качестве значения там находится функция (а точнее ссылка на неё):
alert(sayHello); // function sayHello() { alert("Привет!"); }
Ссылку на функцию вы можете сохранить в любой переменной, например:
var fun = sayHello; // Имя функции указываем без скобок
fun(); // Вызываем функцию sayHello() по ссылке
«Классическое» объявление функции function имя(параметры) {код}
называется в спецификации языка «Function Declaration».
Существует альтернативный синтаксис для объявления функции «Function Expression» (функциональное выражение) – это объявление функции, которое является частью какого-либо выражения (например присваивания):
var sayHello = function () {
alert("Привет!");
};
//вызов функции
sayHello();
Как видите, эта конструкция уже не выглядит стандартной: функция не имеет имени и находится в правой части команды присваивания переменной sayHello
. Эту функцию тоже можно вызвать – на этот раз через переменную sayHello
.
Если у функции нет имени, как в предыдущем примере, она называется «анонимной».
Разница между представленными объявлениями заключается в том, что функции, объявленные как «Function Declaration», создаются интерпретатором до начала выполнения кода (на этапе анализа), поэтому их можно вызвать до объявления:
// Вызов функции до её объявления
sayHello("John"); // Привет, John
function sayHello(name) {
alert( "Привет, " + name );
}
Функции «Function Expression» (функциональные выражения) создаются в процессе выполнения выражения, в следующем примере функция будет создана при операции присваивания sayHi = function...
, поэтому вызов функции до её объявления приведёт к ошибке:
// Вызов функции до её объявления
sayHello("John"); // ошибка!
var sayHello = function(name) {
alert( "Привет, " + name );
}
Параметры и аргументы
Параметр функции – это переменная в функции, которая будет содержать передаваемое снаружи входное значение. Параметры функции перечислены внутри круглых скобок ()
в определении функции.
Для определения параметров функции используется следующий синтаксис:
function name_func(parameter1, parameter2, parameter3...) {
тело функции
}
Параметры следует рассматривать как объявления локальных переменных в теле функции, каждая из которых инициализируются в момент вызова функции соответствующим значением из списка аргументов (или значением по умолчанию).
Аргументы функции — это значения, которые вы предоставляете функции в момент её вызова.
Для вызова функции с передачей ей аргументов используется следующий синтаксис:
name_func(argument1, argument2, argument3...)
Например, при вызове функции sum ей передаются два аргумента:
Аргументы, передаваемые функции при её вызове, присваиваются параметрам функции в том порядке, в каком они указаны: первый аргумент присваивается первому параметру, второй аргумент – второму параметру и т. д.
Важной особенностью функций в JavaScript является то, что функция может вызываться с произвольным количеством аргументов вне зависимости от того, сколько параметров было указано при её объявлении:
- Если при вызове функции ей передаётся больше аргументов, чем задано параметров, то «лишние» аргументы просто игнорируются и не присваиваются ни одному из параметров данной функции.
- Если количество аргументов, передаваемых функции при её вызове меньше, чем было указано параметров при объявлении, то параметрам без соответствующих аргументов присваивается значение
undefined
. Подобное поведение JavaScript удобно использовать, если некоторые аргументы функции необязательны и могут опускаться.
Типы аргументов, передаваемых функции при её вызове, могут быть как примитивами (строки, числа, логические величины (boolean)), так и объектами (в т.ч. массивы и функции).
Важно помнить, что при передаче функции аргументов, представляющих собой примитивные значения, в теле функции создается копия переданного значения и оригинал не изменяется. Иными словами, если аргумент будет изменён внутри функции, после ее завершения эта локальная переменная уничтожается, а оригинальная переменная остается прежней.
Например, в коде ниже есть внешняя переменная user
, значение которой при запуске функции копируется в параметр функции с тем же именем. Далее функция работает уже с параметром:
При передаче в качестве аргумента объекта все происходит не так, как было с примитивными значениями. В таком случае копия объекта не создается – объекты передаются в функцию по ссылке. Переприсваивание самой ссылки также не будет иметь внешнего эффекта, но если в теле функции изменяются свойства объекта, то эти изменения будут видимы вне функции (побочный эффект). Поэтому, всегда следует помнить, что в функции содержится оригинальный объект.
В этом примере мы видим, что внутри функции myFunc
был изменен объект obj
, и эти изменения повлияли на этот объект за пределами функции.
Возвращаемые значения
Функции могут не только принимать входящие данные из внешней программы, но и способны передавать обратно значения по завершении своих инструкций. Для возврата значения используется инструкция return.
Инструкция return имеет следующий синтаксис:
В программу, вызвавшую функцию, возвращается не само выражение, а результат его вычисления. Например, функция sum
возвращает сумму двух чисел:
Для дальнейшего использования возвращаемого значения, результат выполнения функции здесь присвоен переменной z
.
Инструкция return
может быть расположена в любом месте функции. После инструкции return
происходит выход из функции. Все операторы, расположенные в коде после инструкции return
, не будут выполнены, например:
В инструкции return
можно указать сразу выражение, перепишем нашу
функцию sum
:
Внутри функции можно использовать несколько инструкций return
:
В случае, если в функции нет инструкции return
или return
указана без аргументов, то функция вернёт значение undefined
:
Объект arguments
В JavaScript можно создавать функции с произвольным числом аргументов. Доступ ко всем указанным при вызове функции аргументам, в том числе к лишним аргументам, которым не были назначены параметры, осуществляется через объект arguments, который доступен только внутри тела функции.
Свойство length
этого объекта содержит число переданных аргументов.
Напишем функцию, вычисляющую сумму произвольного числа аргументов:
Объект arguments
является массивоподобным объектом, который позволяет передать функции значения аргументов, извлекаемые не по имени, а по номеру.
Поскольку он похож на массив, обратиться к переданным функции аргументам можно так же, как и к элементам массива: первый аргумент будет доступен в теле функции как элемент массива arguments[0],
второй аргумент
будет доступен как элементarguments[1] и т.д.
Функция, приводимая в следующем примере, возвращает строку, содержащую значения параметров (переданные аргументы) и их общее количество, которые были указаны в вызове функции (не в ее определении):
«Поднятие» переменных и функций
Интерпретатор JavaScript всегда перемещает («поднимает») объявления функций и переменных в начало области видимости. Если переменная определена внутри функции, она поднимается к верхней части функции, а если переменная определена глобально — к верхней части глобального контекста. Рассмотрим, что это значит на примере:
var name = "John";
function showName() {
alert( "First Name: " + name );
var name = "Stiv";
alert( "Last Name: " + name );
}
showName();
// First Name: undefined
// Last Name: Stiv
Объявление переменной name
поднимается вверх без присваивания, перекрывая одноименную глобальную переменную, поэтому в первом случае значение переменной name
– undefined
.
В случае с функциями, поднимается вся функция целиком, если речь идет о функциях-объявлениях (declaration); функция-выражение (expression) не поднимается:
function testFunc() {
myFunc(); // "Эта инструкция сработает!"
anomFunc(); // Uncaught TypeError: anomFunc is not a function
var anomFunc = function() { // функциональное выражение «Function Expression»
alert( "Эта инструкция не сработает!" );
}
function myFunc() { // объявление функции «Function Declaration»
alert( "Эта инструкция сработает!" );
}
}
testFunc();
В этом этом примере поднимается только функция myFunc
. Идентификатор «anomFunc» (переменная, которой присвоена функция) также поднимается, но сама анонимная функция при этом остаётся на месте (как и в случае с пременной).
Итоги
- «Классическое» объявление функции («Function Declaration») обрабатывается раньше остального кода. Такую функцию можно вызвать до её объявления.
- Функциональные выражения («Function Expression») обрабатываются вместе с остальным кодом. Такую функцию нельзя вызвать до её объявления.
- Функции в JavaScript являются значениями. Их можно присваивать, передавать, создавать в любом месте кода. В отличие от других видов значений функцию можно вызвать для выполнения кода, содержащегося в ее теле.
- Процесс вызова функции, созданной по объявлению («Function Declaration»), не отличается от процесса вызова функции, созданной по выражению («Function Expression»).
- Ссылки на функции могут храниться в переменных.
- Передаваемые при вызове функции аргументы копируются в параметры функции и становятся локальными переменными.
- Функция может возврвщать значение с помощью инструкции
return (значение)
. - После инструкции
return
происходит выход из функции. - Если оператор
return
вызван без значения, или в теле функции не указанreturn
, то её результат равенundefined
. - Функция представляет из себя определённое действие, поэтому в качестве имен функций выбираются глаголы или фразы, начинающиеся с глаголов.
Задачи
-
Максимальное число
Пользователь вводит последовательно два числа. Написать функцию, которая возвращает максимальное из двух целых чисел, полученных в качестве аргумента.
-
Сравнение двух чисел
Написать функцию, которая запрашивает у пользователя два целых числа и возвращает результат их сравнения в виде одного из знаков: >, < или =..
-
Строка из звёэдочек
Напишите функцию, которая выводит на экран строку, состоящую из звездочек
*
. Длина строки (количество звездочек) является параметром функции. Пусть функция нарисует строку из десяти звёздочек.
-
Вывести на экран все натуральные числа до заданного
Дано натуральное число. Вывести на экран все натуральные числа до заданного включительно. В качестве входного аргумента при запуске функции используйте число 12.
0 1 2 3 4 5 6 7 8 9 10 11
Показать решение
Решение:
Так как нам необходимо выводить натуральные числа, это означает, что вывод должен всегда начинаться с единицы, и при этом выводятся все следующие за ней натуральные числа до тех пор, пока значение переменной цикла не достигнет конечного
n
(на последнем шаге значение переменной цикла будет равноn
).
-
Перепишите функцию, используя оператор ‘?’
Следующая функция возвращает
"Привет мир!"
, если параметрspeech == ru
. В ином случае она возвращает"Hello world!"
. Перепишите функцию, чтобы она делала то же самое, но безif
, используя оператор'?'
.function getGreeting(speech) { if (speech == ru) { return "Привет мир!"; } else { return "Hello world!"; } }
Показать решение