
Ако програмирате на JavaScript, рано или късно ще се блъснете стремглаво в функции със стрелки и известната thisИ често изобщо не е ясно какво се случва отдолу. Между класическите функции, методи, обратни извиквания и обекти е лесно да се объркате от контекста и да се окажете с дебъгване на нещо, което изглежда „магическо“ с часове.
В следващите редове ще разглобим цялата тази „магия“ и ще видим, спокойно, но без да се заобикаляме, Как работят стрелковите функции, по какво се различават от нормалните функции и каква е действителната им връзка с това?Освен това ще разгледаме ролята на методите и функциите като цяло, за да имате пълна представа и да можете да пишете модерен, изчистен и лесен за поддръжка код.
Основи: Какво е функция в JavaScript?
В JavaScript, функцията е основно блок от код за многократна употреба, дефиниран за изпълнение на конкретна задачаСлед като го декларирате, можете да го извиквате колкото пъти искате, с различни входни стойности, без да повтаряте един и същ код отново и отново.
Когато дефинирате класическа функция, използвате ключовата дума функция, незадължително име и списък с параметри в скоби. Параметрите представляват входните данни, а върнатата стойност представлява изходните данни.Нещо толкова типично, като събирането на две числа, изглежда така:
function suma(a, b) {
return a + b;
}
let resultado = suma(5, 3);
console.log(resultado); // 8
В този фрагмент от код, функцията Suma Той получава два параметъра, айб, изпълнява вътрешната операция и връща резултата с връщанекоито след това можем да съхраним в променлива или да използваме директно.
Функциите могат да имат и параметри със стойности по подразбиранеТова е много полезно за избягване на грешки, когато кодът извиква функцията без всички очаквани аргументи:
function saludo(nombre = "desconocido") {
return `¡Hola, ${nombre}!`;
}
console.log(saludo()); // ¡Hola, desconocido!
console.log(saludo("Alice")); // ¡Hola, Alice!
Благодарение на настройките по подразбиране, Вашата функция става по-стабилна и толерантна към непълни повиквания.спестявайки ви ръчни проверки в тялото на функцията.
Отвъд синтаксиса, разумното използване на функциите води до модулност, възможност за многократна употреба и подобрена четимостРазбивате голям проблем на малки, лесни за разбиране, тествани и поддържани части. Много по-лесно е да тествате изолирана функция, отколкото огромен блок код, смесен с всичко останало.
Нормални функции, методи и техният контекст
Преди да се потопим във функциите със стрелки, е полезно да разберем как се държат те. традиционни функции по отношение на контекста И каква е разликата между „свободните“ функции и методите, свързани с обекти?
Нормална функция, декларирана с функция Има поведение на тази динамикаСтойността на „this“ зависи от това как се извиква функцията, а не от това къде е написана в кода. Ако я извикате като самостоятелна функция, „this“ може да сочи към глобалния обект (в нестрог режим) или да бъде недефинирана (в строг режим). Ако я извикате като метод на обект, „this“ обикновено се отнася до този обект.
Когато тази функция е част от обект, обикновено говорим за методМетодът е просто функция, съхранена в свойство на обекта. В този контекст това обикновено сочи към самия обект. когато извикаме метода, използвайки точкова нотация:
const persona = {
nombre: "Laura",
saludar: function () {
console.log("Hola, soy " + this.nombre);
}
};
persona.saludar(); // "Hola, soy Laura" (this => persona)
Ключовата разлика между функция и метод не е техническа (и двете са функции „под капака“), а къде се съхраняват и как се извикват„Хлабавите“ функции се извикват по име, докато методите се извикват чрез обекта и това пряко влияе върху стойността им.
Освен това, нормалните функции могат да се използват като строители с новиТова създава екземпляри на обекти със споделен прототип и те имат обекта на разположение. аргументи което включва всички минали аргументи, дори ако те не са били изрично дефинирани като параметри.
Функции със стрелки: съвременен синтаксис
С ECMAScript 6 (ES6) дойде функции със стрелки, наричани още функции със стрелкикоито предлагат много по-сбит начин за писане на функции, особено когато са кратки или се използват като обратни извиквания.
Най-простата форма на стрелкова функция замества думата функция по стрелката =>и в много случаи позволява имплицитно връщане, като същевременно спестява и връщане и ключовете, когато тялото е единичен израз:
const multiplicacion = (x, y) => x * y;
console.log(multiplicacion(4, 5)); // 20
Тези типове функции обикновено се декларират като изрази, съхранявани в константи или променливизащото по дизайн те са анонимни (нямат собствено име като класическите декларирани функции) и не се използват като декларации на функции с повишени права.
Общият синтаксис може да се обобщи по следния начин: параметри в скоби, стрелка => и след това израз или блок кодАко имате само един параметър, можете да пропуснете скобите; ако имате няколко, те са задължителни:
// Un solo parámetro, sin paréntesis
const doble = n => n * 2;
// Varios parámetros, paréntesis obligatorios
const sumar = (a, b) => a + b;
Когато логиката вече не се вписва в един израз и са ви необходими няколко твърдения, Използвате къдрави скоби и изричен оператор за връщане., точно както в традиционна функция:
const sumaCompleja = (a, b) => {
let resultado = a + b;
resultado = resultado * 2;
return resultado;
};
Функциите със стрелки също приемат параметри със стойности по подразбиранеТова позволява дефинирането на ясни начални стойности без необходимост от допълнителни вътрешни проверки:
const cuadrado = (lado = 10) => lado * lado;
console.log(cuadrado()); // 100
console.log(cuadrado(5)); // 25
Тъй като функционират като изрази, е обичайно да има няколко функции със стрелки, съхранявани в различни променливи за да ги използвате повторно по-късно, например:
const duplicar = n => n * 2;
const triplicar = n => n * 3;
console.log(duplicar(3)); // 6
console.log(triplicar(3)); // 9
Имплицитно връщане и практически синтаксис
Едно от големите предимства на функциите със стрелки е имплицитно връщане, когато тялото се редуцира до един изразТова прави много често изпълняваните задачи, като например трансформиране на стойности, изключително чисти:
const duplicar = n => n * 2;
Този пример е функционално еквивалентен на писането на нормална функция, която използва връщане в рамките на блокНо си спестявате няколко думи и реда код:
const duplicar = function (n) {
return n * 2;
};
Когато искате да върнете буквален обект От функция тип „стрелка“ с имплицитно връщане, трябва да оградите обекта в скоби, за да предотвратите интерпретирането на скобите като начало на блок от код:
const obtenerUsuario = () => ({
nombre: "Laura",
edad: 28
});
С този синтаксис, функциите със стрелки стават идеални инструменти за писане на кратък и изразителен код, широко използван в методи за масиви, обещания, леко функционално програмиране и компонентна логика в съвременните рамки.
Това във функциите със стрелки: лексикален контекст
Истинската разлика между стрелкова функция и класическа функция не е само в синтаксиса: тя е в как се справят със стойността на товаДокато нормалните функции имат динамично „това“, което зависи от името им, стрелковите функции имат този лексикон.
Да се каже, че „това“ е лексикално, означава, че Функцията със стрелка не създава свой собствен вътрешен thisВместо това, той наследява „това“ на контекста, в който е дефиниран. Ако напишете стрелка вътре в нормална функция, „това“ на стрелката ще бъде същото като това на външната функция; ако сте в глобалния обхват на модул, той наследява този контекст.
Това има пряко въздействие, когато използвате стрелкови функции като методи на обекти. Ако например напишете нещо подобно:
const obj = {
nombre: "Juan",
imprimir: () => {
console.log(this.nombre);
}
};
obj.imprimir();
В този фрагмент от код, `this` във функцията със стрелка не се отнася до `obj`а по-скоро към външния контекст, където е създадена тази стрелка (например глобалният обект или текущият модул). Обичайният резултат е, че името е недефинирано или не сочи към очакваната стойност.
Лексикалното наследство на това, от друга страна, Благословия е да се работи с обратни извиквания вътре в методиВместо да се налага да се правят трикове като съхраняване на `this` в междинна променлива (`var that = this`) или използване на `.bind()`, стрелката без усилие поддържа `this` от горния контекст:
function Contador() {
this.valor = 0;
setInterval(() => {
this.valor++;
console.log(this.valor);
}, 1000);
}
new Contador();
В този пример функцията, предадена на setInterval е стрелкаТака че, в него „това“ си остава инстанция на Counter, което значително улеснява работата и избягва типичните грешки с контекста.
В допълнение към това, функциите със стрелки също не създават своя собствена версия на аргументи на обектаСледователно, ако трябва да получите достъп до всички динамично предавани аргументи, ще трябва да използвате оператор за почивка (…параметри) или да се прибегне до нормална функция, ако е уместно.
Лексикален обхват и затваряне със стрелкови функции
Друго следствие от дизайна на стрелката е, че споделят същия лексикален обхват като родителския си контекстТова се забелязва не само при „това“, но и при променливите, до които имат достъп, което се вписва много добре в концепцията за затваряния.
Помислете за функция, която връща друга вътрешна функция. Ако вътрешната функция е стрелка, Ще можете да продължите да използвате променливите, дефинирани във външната функциядори ако вече е приключило изпълнението си:
const exterior = () => {
const variableExterna = "Valor exterior";
return () => {
console.log(variableExterna);
};
};
const interna = exterior();
interna(); // "Valor exterior"
В този случай вътрешната функция е стрелка, която се възползва от същия лексикален обхват за достъп до външни променливи без проблеми. Този модел се използва широко за създаване на конфигурирани функции, фабрики и капсулирана логика.
Стрелкови функции спрямо нормални функции: сравнение
Ако поставим класическа функция и стрелка една до друга, виждаме няколко съществени разлики, които влияят на начина, по който ги използваме в ежедневната практика:
- Синтаксис и краткост: нормалната функция се записва като
function nombre() {}, докато стрелката обикновено еconst nombre = () => {}В прости случаи стрелката е очевидно по-къса и по-четлива. - Подемно-транспортно средство: традиционните декларации на функции са те издигатСледователно, можете да ги извикате преди тяхното дефиниране в кода. Стрелките, които са изрази, присвоени на променливи или константи, Те не могат да бъдат използвани, преди да бъдат дефинирани.
- Контекст на това: Класическата функция има динамично „това“, което се променя в зависимост от това как се извиква; стрелката има лексикално „това“, наследено от контекста, в който е декларирана.
- Използвайте като строител: нормалните функции могат да бъдат извикани с нов за създаване на обекти; функциите със стрелки те не могат да се използват като строители.
- Аргументи на обекта: В традиционните функции имате аргументи; в стрелките този обект не съществува и обикновено се използват останали параметри.
Поради всички тези причини, най-често срещаното е Използвайте стрелкови функции за обратни извиквания, бързи трансформации на данни и логика, където не ви е необходимо ваше собствено „това“.и поддържайте класически функции за конструктори, обектни методи, които изискват динамично `this`, или случаи, в които наистина се интересувате от обекта arguments.
Често срещани приложения на функции със стрелки
В съвременния JavaScript, ежедневната употреба е пълна със стрелкови функции, особено в обратни извиквания, методи за масиви и функционален кодТе са естественият заместител на анонимните функции, изпълнявани през целия живот.
Много типична употреба е да се представят като обратни извиквания в методи за масиви като например forEach, map, filter или reduce. Синтаксисът става много по-кратък и, наследявайки го, те се вписват по-добре в много сценарии:
const nombres = ;
nombres.forEach(nombre => {
console.log(`Hola ${nombre}`);
});
В сравнение с писането на същото нещо с помощта на функция, Версията със стрелката е много по-изчистена. и не губи четливост:
.map(function (n) {
return n * 2;
});
.map(n => n * 2);
Когато правите по-сложни трансформации, методи като филтър и карта Те също така очевидно се възползват от синтаксиса на стрелките:
const nums = ;
const pares = nums.filter(n => n % 2 === 0);
const duplicados = nums.map(n => n * 2);
console.log(pares); //
console.log(duplicados); //
Стрелките също често се използват в обратни извиквания на таймер или събитие...при условие че обработката на „това“, която предоставят, отговаря на вашите нужди. В „setTimeout“, например, е много често срещано да се види:
setTimeout(() => {
console.log("Mensaje con delay");
}, 1000);
В случай на итерация през масиви с forEach и функции със стрелкиКомбинацията е особено удобна за извършване на прости операции върху всеки елемент, без да се губи яснота:
const nombres = ;
nombres.forEach(nombre => console.log("Hola " + nombre));
Кога е подходящо и кога не е да се използват функции със стрелки?
Предвид всичко гореизложено, изглежда, че стрелковите функции са решението на всичко, но реалността е такава Те не винаги са най-добрият изборИма специфични ситуации, в които е за предпочитане да се продължи с традиционните функции.
Функциите със стрелки светват, когато имате нужда от тях. кратки обратни извиквания, трансформации на данни, композиция на функции и наследяване на „това“ от средатаВ тези случаи избягвате шаблонни шаблони, опростявате кода и улеснявате следването на логиката на вашата програма.
Въпреки това, Не бива да ги използвате като конструкторизащото не работят директно с `new`. Ако възнамерявате да създавате персонализирани типове, класове или прототипи, продължете да използвате нормалните функции или синтаксиса `class`.
Също така не е добра идея да се декларира методи на обекти като стрелка, ако очаквате „това“ да сочи към този обектВече видяхме, че стрелката не дефинира собственото си „това“, така че стойността няма да е това, което много разработчици очакват, когато четат кода, което води до фини грешки.
Дори в неща като Някои обработчици на събития или API базират поведението си на стойността на това.Нормална функция може да е по-подходяща, именно защото в този случай е важно това да се променя в зависимост от това кой извиква функцията.
И накрая, ако имате нужда от гъвкав достъп до всички аргументи, получени чрез аргументиМоже да ви се стори по-естествено да използвате традиционна функция или да препроектирате интерфейса на функцията си, за да използвате REST параметри.
Функции и методи в дизайна на JavaScript приложения
След като разберете разликите между функции със стрелки, класически функции и методиНаистина можете да се възползвате от тях, за да структурирате цялостни JavaScript приложения, както фронтенд, така и бекенд.
„Нормалните“ функции са отличен ресурс за задачи с общо предназначение, създаване на обекти (конструктори) и логика, където е необходим прецизен контрол върху товаВъзможността му за повдигане може също да опрости организацията на кода в големи файлове, ако го подредите внимателно.
Методите, от своя страна, функционират като специфични действия, свързани с данните на даден обектНапълно логично е „това“ да се отнася до самия обект, тъй като те капсулират поведението и състоянието заедно. Когато дефинирате метод в прототип или клас, е естествено да използвате „функция“ или съкратения синтаксис за обектни методи.
Функциите на стрелките влизат в действие като част, която допълва предишните: Те служат за съединяване на части, трансформиране на данни и дефиниране на обратни извиквания без визуален шумТяхното интензивно използване в съвременните библиотеки, рамки и API не е случайно: те се вписват много добре в декларативния стил на голяма част от днешния код.
Разбирането на ролята на всеки тип функция, знанието кога да се използва едната или другата и овладяването на поведението на „this“ ви позволява да пишете По-стабилен, по-чист и по-лесен за мащабиране JavaScript код, като избягва много от класическите езикови капани.
Всичко, което видяхме за класическите функции, параметри, методи, стрелкови функции, лексикален обхват и специалното обработване на „това“ вече ви дава солидна основа за справяне с реални проекти: от малки скриптове дори сложни приложения, Изборът на правилния тип функция, която да се използва във всяка част, прави разликата между хаотичния код и кода, който всеки може да чете, поддържа и с удоволствие да подобрява..