Saltar al contingut

Funcions

A continuació tens un exemple de declaració d’una funció:

function sum(a: number, b: number): number {
return a + b
}

La paraula reservada function s’utilitza per definir una funció.

A continuació va el nom de la funció:

function sum(a: number, b: number): number {
///
return a + b
}

A continuació es defineixen els paràmetres de la funció amb el seu tipus:

function sum(a: number, b: number): number {
//////////////////////
return a + b
}

Es pot declarar el tipus del valor que retorna la funció (opcional):

function sum(a: number, b: number): number {
////////
return a + b
}

I a continuació és declara el cos de la funció (entre {})

La paraula return finalitza l’execució de la funció i retorna el valor corresponent

Invocar una funció és molt senzill:

const result = sum(3, 4)
console.log(result)

El cos d’una funció pot utilitzar totes les diferents característiques del llenguatge:

  • variables
  • expressions if/ else
  • bucles while i for
  • invocar altres mètodes
  • definir altres funcions

A continuació tens un exemple:

function happySum(a: number, b: number) {
function double(a: number) {
return a * 2
}
if (a > 5) {
a += 1000
}
return double(a + b)
}
const result = happySum(10, 50)
console.log(result) // 2120

Potser el que més et sorpren és que pots declarar una funció dins d’una funció 😯

Una variable declarada dins d’una funció només és visible dins de la funció.

Per això tenen el nom de variables locals (a la funció)

Per exemple,

function showMessage() {
let message = "Hello, I'm TypeScript!" // local variable
console.log(message)
}
showMessage() // Hello, I'm JavaScript!
console.log(message) // <-- Error! The variable is local to the function

En canvi, una variable declarara fora d’una funció és accessible per qualsevol funció.

let userName = 'John'
function showMessage() {
let message = `Hello, ${userName}`
alert(message)
}
showMessage() // Hello, John

Aquestes variables s’anomenen variables globals, i no s’han d’utilitzar.

La funció té accés complet a la variable externa.

També la pot modificar:

let userName = 'John'
function showMessage() {
userName = 'Bob'
let message = `Hello, ${userName}`
alert(message)
}
showMessage() // Hello, Bob

La variable externa només s’utilitza si no n’hi ha una local.

Si es declara una variable amb el mateix nom dins de la funció, aquesta fa “ombra” a l’externa.

Per exemple, en el següent codi la funció utilitza la variable local userName i la variable exterior s’ignora:

let userName = 'John'
function showMessage() {
let userName = 'Alice'
let message = `Hello, ${userName}`
alert(message)
}
showMessage() // Hello, Alice

Podem passar dades arbitràries a les funcions utilitzant paràmetres.

En el següent exemple, la funció té dos paràmetres: from i text.

function showMessage(from: string, text: string) { // parameters: from, text
console.log(`${from}: ${text} `)
}
showMessage('Ann', 'Hello!') // Ann: Hello!
showMessage('Ann', "What's up?") // Ann: What's up?

Quan es crida la funció, els valors donats es copien a les variables locals from i text. I la funció les utilitza.

Aquí tens un altre exemple: tenim una variable from i la passem a la funció. Tingues en compte que: la funció canvia from, però el canvi no es veu a fora, perquè una funció sempre obté una còpia del valor:

function showMessage(from: string, text: string) {
from = from.toUpperCase() // hace que "from" se vea mejor
console.log(`${from}: ${text}`)
}
let from = "Ann"
showMessage(from, "Hello") // ANN: Hello
// el valor de "from" es el mismo, la función modificó una copia local
console.log(from) // Ann

Quan un valor es passa com a paràmetre d’una funció, també s’anomena argument.

Per deixar els termes clars:

  • Un paràmetre és una variable llistada dins dels parèntesis en la declaració de la funció (és un terme per al moment de la declaració)

  • Un argument és el valor que es passa a la funció quan aquesta és cridada (és el terme per al moment en què es crida).

Declarem funcions llistant els seus paràmetres, després les cridem passant-los arguments.

En l’exemple anterior, es pot dir: “la funció es declara amb dos paràmetres, i després es crida amb dos arguments: from i "Hola"”. showMessage

Una funció pot retornar un valor al codi que la crida com a resultat.

L’exemple més simple seria una funció que suma dos valors:

function sum(a: number, b: number) {
return a + b
}
let result = sum(1, 2)
console.log(result) // 3

La directiva return pot estar a qualsevol lloc de la funció. Quan l’execució l’assoleix, la funció s’atura i el valor es retorna al codi que l’ha cridat (assignat al result anterior).

Pot haver-hi molts return en una sola funció. Per exemple:

function checkAge(age: number) {
if (age > 18) {
return true
} else {
return confirm('¿Tienes permiso de tus padres?')
}
}
let age = Number(prompt('¿Qué edad tienes?', "18"))
if (checkAge(age)) {
alert('Acceso otorgado')
} else {
alert('Acceso denegado')
}

És possible utilitzar return sense cap valor. Això fa que la funció surti o acabi immediatament.

Per exemple:

function showMovie(age: number) {
if ( !checkAge(age) ) {
return
}
alert( "Mostrándote la película" )
// ...
}

Una funció pot tenir paràmetres opcionals.

Has d’utilitzar el símbol ? per marcar un paràmetre com a opcional.

function f(x?: number) {
if (x != undefined)
console.log(x + 10)
}
f() // OK
f(10) // OK

Encara que el paràmetre s’ha especificat com de tipus number, el paràmetre en realitat té el tipus number | undefined.

Els paràmetres opcionals s’han de posar al final.

Una altra opció és proporcionar un valor per defecte:

function f(x: number = 10) {
console.log(x + 10)
}
f() // 20
f(10) // 20

Ara el paràmetre x sempre serà de tipus number perqué qualsevol argument undefined es reemplaza pel valor 10.

A més, quan un paràmetre és opcional, si vols pots passar undefined com argument i el resultat serà el mateix:

function f(x: number = 10) {
console.log(x + 10)
}
f() // 20
f(10) // 20
f(undefined) // 20

Pots definir funcions que prenen un nombre il·limitat d’arguments utilitzant paràmetres rest.

Un paràmetre rest apareix després de tots els altres paràmetres i utilitza la sintaxi ...:

function max(x: number, ...xs: number[]) {
let result = x
for (const x of xs) {
if (x > result)
result = x
}
return result
}
console.log(max(10)) // 10
console.log(max(10, 30, 15)) // 30

A continuació pots veure com pots crear una funció anònima:

(function () { console.log("Hello") })

El motiu pel qual s’anomena anònim és perquè no està assignat a una variable i, per tant, no té nom.

Si executes el codi no passa res:

Terminal window
> deno test.ts

Si vols, pots crear i executar la funció amb ():

(function () { console.log("Hello") }) ()
//

Ara, si executes el codi, es crea la funció anònima i s’executa:

Terminal window
> deno test.ts
Hello

Tanmateix, una funció anònima, també coneguda com a literal de funció, es pot assignar a una variable per crear una variable de funció:

const sayHi = function() { console.log("Hello") }

Això crea una variable de funció anomenada sayHi.

En aquesta expressió, la funció literal original es troba a la part dreta del símbol =:

const sayHi = function() { console.log("Hello") }
///////////////////////////////////

i el nou nom de la variable es troba al costat esquerre:

const sayHi = function() { console.log("Hello") }
/////

Una funció anònima és com qualsevol funció: pot prendre paràmetres i retorna valors

(function (a: number) { return a * 2 })

I es crida com qualsevol funció:

const result = (function (a: number) { return a * 2 }) (2)
console.log(result) // 4

I la podem asignar a una variable:

const double = function (a: number) { return a * 2}

Igual que la llista de paràmetres d’una funció, això significa que la variable de funció double pren un paràmetre, un number anomenat a.

Ara pots invocar la funció double així:

const double = function (a: number) { return a * 2}
const result = double(6)
console.log(result) // 12

A més, quan tens altres funcions del tipus number => number les pots emmagatzemar en una llista:

const fs = [
(function (a: number) { return a * 2}),
(function (a: number) { return a * 3})
]
for (const f of fs) {
console.log(f(4))
}

Si executes el codi, pots veure que s’executen totes les funcions de la llista:

Terminal window
> deno test.ts
8
12

Un exemple una mica absurd, però així pots començar a entendre que una funció anònima és un valor que es pot tractar com qualsevol altre valor.

Totes les funcions són un valor, no només les anònimes, i poden estar referenciada per altres variables.

A continuació tens un exemple:

function sayHi() {
console.log("Hello")
}
const sayHello = sayHi
sayHello() // Hello
sayHi() // Hello

El mateix es pot fer amb una variable de funció:

const sayHi = function() {
console.log("Hello")
}
const sayHello = sayHi
// ...

TODO explicar inicialització

Una funció d’ordre superior (“higher-order function”) és una funció que pren altres funcions com a paràmetres d’entrada o retorna una funció com a resultat.

Escriure una funció que pren paràmetres de funció

Section titled “Escriure una funció que pren paràmetres de funció”

Per crear una funció que pren un paràmetre de funció, tot el que has de fer és:

  1. A la llista de paràmetres de la teva funció, defineix la signatura de la funció que vols acceptar
  2. Utilitza aquesta funció dins de la teva funció

Per demostrar-ho, aquí tens una funció que pren un paràmetre d’entrada anomenat f, on f és una funció:

function sayHello(f: ()=> void) {
f()
}

El tipus del paràmetre f indica que f és una funció, i defineix els tipus de funcions que la funció sayHello accepta:

  • f és el nom del paràmetre d’entrada de la funció. És el mateix que anomenar un paràmtre string com so un parametre number com n.
  • La signatura de tipus de f especifica el tipus de funcions que acceptarà aquest funció.
  • La part () de la signatura de f (a la part esquerra del símbol =>) indica que f no pren paràmetres d’entrada.
  • La part void de la signatura (a la part dreta del símbol =>) indica que f no ha de retornar cap resultat.

Dins de la funció sayHello, s’invoca la funció f que s’ha passat com a paràmetre.

Ara que has definit sayHello, crea una funció que coincideixi amb la signatura de f perquè puguem provar-la.

La funció següent no pren paràmetres d’entrada i no retorna res, de manera que coincideix amb la signatura de tipus de f:

function helloDavid() {
console.log("Hello, David")
}

Com que les signatures de tipus coincideixen, pots passar helloDavid a sayHello:

function sayHello(f: () => void) {
f()
}
function helloDavid() {
console.log("Hello, David")
}
sayHello(helloDavid) // prints "Hello, David"

Felicitats 😊! Acabes de definir una funció anomenada sayHello que pren una funció com a paràmetre d’entrada i després invoca aquesta funció al cos de la funció.

Però el més important no és que sayHello pugui prendre una funció com a paràmetre d’entrada; és que pot prendre qualsevol funció que coincideixi amb la signatura de f.

Per exemple, com que la següent funció no pren paràmetres d’entrada i no retorna res, també funciona amb sayHello:

function bonjourJuliet() {
console.log("Bonjour, Juliet")
}
Terminal window
> deno test.ts
Bonjour, Juliet

En aquesta funció:

function sayHello(f: () => void) {
f()
}

Has observat que la signatura de tipus de f és:

() => void

Saps que això significa, “una funció que no pren paràmetres d’entrada i no retorna res (void)”.

A continuació tens una funció que pren dos paràmetres de tipus number i retorna un number:

f: (a: number, b: number) => number

T’imagines quin tipus de funcions coincideixen amb aquesta signatura?

La resposta és que qualsevol funció que pren dos paràmetres number d’entrada i retorna un number coincideix amb aquesta signatura, de manera que totes aquestes “funcions” coincideixen:

function sum(a: number, b: number): number {
return a + b
}
function subtract(a: number, b: number): number {
return a - b
}
function multiply(a: number, b: number): number {
return a * b
}

La funció execute accepta com a paràmetre qualsevol de les tres funcions:

function execute(f: (a: number, b: number) => number) {
console.log(f(5, 8))
}
// ...
execute(sum)
execute(subtract)
execute(multiply)

Pots veure que funciona:

Terminal window
> deno test.ts
13
-3
40

Prenent un paràmetre de funció juntament amb altres paràmetres

Section titled “Prenent un paràmetre de funció juntament amb altres paràmetres”

Perquè les funcions d’ordre superior siguin realment útils, també necessiten algunes dades per treballar-hi: per tant, també ha d’acceptar dades com a altres paràmetres d’entrada.

Per exemple, aquí tens una funció anomenada executeNTimes que té dos paràmetres d’entrada: una funció i un number:

function executeNTimes(f: () => void, n: number) {
let i = 0
while (i < n) {
f()
i += 1
}
}

Com mostra el codi, executeNTimes executa la funció f n vegades. Com que un bucle simple com aquest no té valor de retorn, executeNTimes retorna void.

Per provar executeNTimes, defineix una funció que coincideixi amb la signatura de f:

function helloWord() {
console.log("Hello, world")
}

A continuació, passa aquesta funció a executeNTimes juntament amb un number:

executeNTimes(helloWord, 3)

Si executes el codi:

Terminal window
> deno test.ts
Hello, world
Hello, world
Hello, world

Excel·lent🐱. La funció executeNTimes executa la funció helloWorld tres vegades.

Les teves funcions es poden continuar complicant-se com sigui necessari.

Per exemple, aquesta funció pren una funció de tipus (number, number) => number, juntament amb dos paràmetres d’entrada:

function executeAndPrint(f: (a: number, b: number) => number, x: number, y: number) {
const result = f(x, y)
console.log(result)
}

Com que les fucions sum i multiply coincideixen amb aquesta signatura de tipus, es poden passar a executeAndPrint juntament amb dos valors number:

function executeAndPrint(f: (a: number, b: number) => number, x: number, y: number) {
const result = f(x, y)
console.log(result)
}
function sum(a: number, b: number): number {
return a + b
}
function multiply(a: number, b: number): number {
return a * b
}
executeAndPrint(sum, 3, 11) // prints 14
executeAndPrint(multiply, 3, 9) // prints 27

Coherència de signatura del tipus de funció

Section titled “Coherència de signatura del tipus de funció”

Una bona part d’aprendre sobre les signatures de tipus de funció és que la sintaxi que utilitzes per definir els paràmetres d’entrada de la funció és la mateixa sintaxi que fas servir per escriure literals de funció.

Per exemple, si has d’escriure una funció que calcula la suma de dos nombres enters, la pots escriure així amb un tipus explícit:

const sum: (a: number, b: number) => number = function (a: number, b: number) {
return a + b
}

Aquest codi consta de la signatura de tipus:

const sum: (a: number, b: number) => number = function (a: number, b: number) {
////////////////////////////////
return a + b
}

Els paràmetres d’entrada:

const sum: (a: number, b: number) => number = function (a: number, b: number): number {
//////////////////////////////
return a + b
}

i el cos de la funció:

const sum: (a: number, b: number) => number = function (a: number, b: number) {
return a + b
/////
}

La consistència de TypeScript es mostra aquí, on aquest tipus de funció:

const sum: (a: number, b: number) => number = function (a: number, b: number) {
////////////////////////////////
return a + b
}

és el mateix que la signatura de tipus que utilitzes per definir un paràmetre d’entrada de funció:

function executeAndPrint(f: (a: number, b: number) => number, x: number, y: number) {
////////////////////////////////
const result = f(x, y)
console.log(result)
}

Hay otra sintaxis muy simple y concisa para crear funciones, que a menudo es mejor que las Expresiones de funciones.

Se llama “funciones arrow”, porque se ve así:

const f = (arg1, arg2, ..., argN) => expression

Esto crea una función f que acepta los parámetros arg1..argN, luego evalúa la expression del lado derecho mediante su uso y devuelve su resultado.

En otras palabras, es la versión más corta de:

const f = function(arg1, arg2, ..., argN) {
return expression
}

Veamos un ejemplo concreto:

let sum = (a:number, b:number) => a + b
/* Esta función de flecha es una forma más corta de:
let sum = function(a: number, b: number) {
return a + b
}
*/
alert( sum(1, 2) ) // 3

Como puedes ver, (a:number, b:number) => a + b significa una función que acepta dos argumentos llamados a y b.

Tras la ejecución, evalúa la expresión a + b y devuelve el resultado.

Si no hay parámetros, los paréntesis estarán vacíos; pero deben estar presentes:

let sayHi = () => alert("¡Hola!")
sayHi()

Las funciones “arrow” se pueden usar de la misma manera que las expresiones de función.

Por ejemplo, para crear dinámicamente una función:

let age = Number(prompt("What is your age?", "18"))
let welcome = (age < 18) ?
() => alert('¡Hola!') :
() => alert("¡Saludos!")
welcome()

Las funciones “arrow” pueden parecer desconocidas y poco legibles al principio, pero eso cambia rápidamente a medida que los ojos se acostumbran a la estructura.

Son muy convenientes para acciones simples de una línea.

Las funciones de flecha que estuvimos viendo eran muy simples. Toman los parámetros a la izquierda de =>, los evalúan y devuelven la expresión del lado derecho.

A veces necesitamos una función más compleja, con múltiples expresiones o sentencias. En ese caso debemos encerrarlos entre llaves. La diferencia principal es que las llaves necesitan usar un return para devolver un valor (tal como lo hacen las funciones comunes).

Como esto:

let sum = (a: number, b: number) => { // la llave abre una función multilínea
let result = a + b
return result // si usamos llaves, entonces necesitamos un "return" explícito
};
console.log(sum(1, 2)) // 3

1. Implementa la funció map, que retorna una llista en que ha aplicat f a tots els elements de xs:

function map(xs: number[], f: (x: number) => number): number[] {
// TODO
}
const result = map([2, 3, 4], a => a + 2)
console.log(result) // [ 4, 5, 6 ]

2. Implementa la funció filter, que retorna una llista amb tots els elements de xs que tornen true quan s’els hi aplica la funció f:

function filter(xs: number[], f: (x: number) => boolean): number[] {
// ...
}
const result = filter([1, 2, 3, 4], a => a > 2)
console.log(result) // [ 3, 4]

3. Implementa la funció reduce, que retorna un únic resultat a partir d’anar afegint a result el que computi la funció f a partir de result i cada valor x:

function reduce(xs: number[], initialValue: number, f: (result: number, x: number) => number): number {
let result = initialValue
for (const x of xs) {
result = f(result, x)
}
return result
}
const result = reduce([1, 5, 10], 0, (a, b) => a + b)
console.log(result) // 16

4. Donada aquesta llista:

const xs = [1, -5, 10, 15, 20]
const result = // ...
console.log(result) // 12000

Amb les funcions anteriors:

  1. Filtra tots els element que són positius
  2. A continuació multiplica per 2 tots els elements que són majors que 10
  3. A continuació multiplica tots els elements de la llista entre ells.

Imagina que vols escriure una funció greet que retorna una funció.

Aquesta funció agafarà un paràmetre de tipus stringp i l'imprimirà amb console.log()`.

Per simplificar aquest primer exemple, greet no prendrà cap paràmetre d’entrada; només crearà una funció i la retornarà.

Tenint en compte aquesta afirmació, pots començar a construir greet. Ja saps que serà una funció:

function greet() {}

També saps que aquesta funció retornarà una funció que (a) pren un paràmetre string i (b) imprimeix aquesta string amb console.log. Per tant, aquesta funció té el tipus string => void:

function greet(): (s: string) => void {
// ...
}

Ara només necessites el cos de la funció. Ja saps que la funció ha de retornar una funció, i aquesta funció pren un string i l’imprimeix. Aquesta funció anònima coincideix amb aquesta descripció:

(name: string) => console.log(`Hello, ${name}`)

Ara només has de tornar aquesta funció des de la funció:

function greet(): (s: string) => void {
return (name: string) => console.log(`Hello, ${name} `)
}

Com que aquesta funció retorna una funció, obtens la funció cridant a `greet()p . Aquest és un bon pas per fer al REPL perquè verifica el tipus de la nova funció:

const greetFunction = greet()

Ara pots invocar a greetFunction:

greetFunction("David") // prints "Hello, David"

Enhorabona, acabes de crear una funció dque retorna una funció i després has executat aquesta funció.

La nostra funció seria més útil si poguéssiu passar una salutació, així que fem-ho. Tot el que has de fer és passar la salutació com a paràmetre a la funció greet i utilitzar-la al string que hi ha dins console.log:

function greet(theGreeting: string): (s: string) => void {
return (name: string) => console.log(`${theGreeting}, ${name} `)
}

Ara, quan invoques la teva funció, el procés és més flexible perquè pots canviar la salutació, tal com pots veure a continuació:

const sayHola = greet("Hola")

sayHola és una funció que pren un paràmetre d’entrada de tipus string i retorna void(res).

const sayHola: (s: string) => void = greet("Hola")

Ara, quan invoques la funció sayHola la sortida és diferent:

sayHola("Laura") // prints "Hello, Laura"

Pots crear tantes funcions diferent com vulguis:

const sayCiao = greet("Ciao")
const sayBonjour = greet("Bonjour")
sayCiao("Laura") // prints "Ciao, Laura"

1. A continuació tens un codi que genera una llista de llistes de números:

{
const xss: number[][] = Array.from({ length: 100 }, () =>
Array.from({ length: 100 },
() => Math.floor(Math.random() * 100000)))
console.log(xss)
}

Escriu un codi que torni la llista en què la suma dels seus números és menor:


El contingut d'aquest lloc web té llicència CC BY-NC-ND 4.0.

©2022-2025 xtec.dev