Szukaj…
Wprowadzenie
Funkcje w JavaScript zapewniają zorganizowany kod wielokrotnego użytku do wykonywania zestawu działań. Funkcje upraszczają proces kodowania, zapobiegają nadmiarowej logice i ułatwiają śledzenie kodu. W tym temacie opisano deklarację i wykorzystanie funkcji, argumentów, parametrów, instrukcji return i zakresu w JavaScript.
Składnia
przykład funkcji (x) {return x}
var example = function (x) {return x}
(function () {...}) (); // Wyrażenie funkcji natychmiast wywołane (IIFE)
var instance = new Example (x);
Metody
fn.apply (valueForThis [, arrayOfArgs])
fn.bind (valueForThis [, arg1 [, arg2, ...]])
fn.call (valueForThis [, arg1 [, arg2, ...]])
ES2015 + (ES6 +):
const example = x => {return x}; // Wyraźny powrót funkcji strzałki
const example = x => x; // Ukryty zwrot funkcji strzałki
const example = (x, y, z) => {...} // Funkcja strzałki wielu argumentów
(() => {...}) (); // IIFE za pomocą funkcji strzałki
Uwagi
Informacje na temat funkcji strzałek można znaleźć w dokumentacji funkcji strzałek .
Działa jako zmienna
Normalna deklaracja funkcji wygląda następująco:
function foo(){
}
Funkcja zdefiniowana w ten sposób jest dostępna z dowolnego miejsca w jej kontekście według nazwy. Ale czasem przydatne może być traktowanie odniesień funkcji jak odniesień do obiektu. Na przykład możesz przypisać obiekt do zmiennej w oparciu o pewien zestaw warunków, a następnie pobrać właściwość z jednego lub drugiego obiektu:
var name = 'Cameron';
var spouse;
if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };
var spouseName = spouse.name;
W JavaScript możesz zrobić to samo z funkcjami:
// Example 1
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };
hash('Fred');
W powyższym przykładzie hash
jest zmienną normalną. Jest przypisane odwołanie do funkcji, po czym funkcja, do której się odwołuje, może być wywołana za pomocą nawiasów, podobnie jak normalna deklaracja funkcji.
Powyższy przykład odwołuje się do funkcji anonimowych ... funkcji, które nie mają własnej nazwy. Zmiennych można także używać do odwoływania się do nazwanych funkcji. Powyższy przykład można przepisać w następujący sposób:
// Example 2
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;
hash('Fred');
function md5Hash(value){
// ...
}
function sha1Hash(value){
// ...
}
Lub możesz przypisać odwołania do funkcji z właściwości obiektu:
// Example 3
var hashAlgorithms = {
sha1: function(value) { /**/ },
md5: function(value) { /**/ }
};
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;
hash('Fred');
Możesz przypisać odwołanie do funkcji obsługiwanej przez jedną zmienną do drugiej, pomijając nawiasy. Może to spowodować łatwy do popełnienia błąd: próbę przypisania wartości zwracanej funkcji do innej zmiennej, ale przypadkowe przypisanie odwołania do funkcji.
// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)
function getValue(){
return 41;
}
Odwołanie do funkcji jest jak każda inna wartość. Jak widzieliście, odwołanie można przypisać do zmiennej, a wartość odniesienia tej zmiennej można następnie przypisać do innych zmiennych. Można przekazywać odwołania do funkcji jak każdą inną wartość, w tym przekazywać odwołanie do funkcji jako wartość zwracaną innej funkcji. Na przykład:
// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');
// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
// return a reference to an anonymous function
if (algorithmName === 'sha1') return function(value){ /**/ };
// return a reference to a declared function
else if (algorithmName === 'md5') return md5;
}
function md5Hash(value){
// ...
}
Nie trzeba przypisywać odwołania do funkcji do zmiennej, aby ją wywołać. W tym przykładzie, budując przykład 5, wywołamy getHashingFunction, a następnie natychmiast wywołamy zwróconą funkcję i przekażemy jej wartość zwracaną do hashedValue.
// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );
Uwaga na temat podnoszenia
Należy pamiętać, że w przeciwieństwie do normalnych deklaracji funkcji, zmienne odwołujące się do funkcji nie są „podnoszone”. W przykładzie 2 funkcje md5Hash
i sha1Hash
są zdefiniowane na dole skryptu, ale są natychmiast dostępne wszędzie. Niezależnie od tego, gdzie zdefiniujesz funkcję, interpreter „podnosi” ją na szczyt zakresu, dzięki czemu jest natychmiast dostępna. Nie dotyczy to definicji zmiennych, więc kod podobny do poniższego ulegnie awarii:
var functionVariable;
hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.
function hoistedFunction(){}
functionVariable = function(){};
Funkcja anonimowa
Definiowanie funkcji anonimowej
Kiedy funkcja jest zdefiniowana, często nadajesz jej nazwę, a następnie wywołujesz ją przy użyciu tej nazwy, na przykład:
foo();
function foo(){
// ...
}
Po zdefiniowaniu funkcji w ten sposób środowisko wykonawcze Javascript zapisuje twoją funkcję w pamięci, a następnie tworzy odwołanie do tej funkcji, używając przypisanej jej nazwy. Ta nazwa jest następnie dostępna w bieżącym zakresie. Może to być bardzo wygodny sposób na utworzenie funkcji, ale JavaScript nie wymaga przypisania nazwy do funkcji. Następujące kwestie są również całkowicie legalne:
function() {
// ...
}
Gdy funkcja jest zdefiniowana bez nazwy, jest znana jako funkcja anonimowa. Funkcja jest przechowywana w pamięci, ale środowisko wykonawcze nie tworzy dla niej automatycznie odwołania do niej. Na pierwszy rzut oka może się wydawać, że coś takiego nie byłoby przydatne, ale istnieje kilka scenariuszy, w których funkcje anonimowe są bardzo wygodne.
Przypisywanie funkcji anonimowej do zmiennej
Bardzo częstym zastosowaniem funkcji anonimowych jest przypisanie ich do zmiennej:
var foo = function(){ /*...*/ };
foo();
To użycie anonimowych funkcji jest szczegółowo omówione w Funkcjach jako zmienna
Dostarczanie funkcji anonimowej jako parametru do innej funkcji
Niektóre funkcje mogą przyjmować odniesienie do funkcji jako parametru. Są one czasami nazywane „wstrzykiwaniem zależności” lub „wywołaniami zwrotnymi”, ponieważ pozwala funkcji wywołać funkcję „oddzwonienia” do kodu, co daje możliwość zmiany sposobu działania wywoływanej funkcji. Na przykład funkcja mapy obiektu Array umożliwia iterację każdego elementu tablicy, a następnie budowę nowej tablicy przez zastosowanie funkcji transformacji do każdego elementu.
var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]
Tworzenie nazwanej funkcji byłoby uciążliwe, niechlujne i niepotrzebne, co zaśmieciłoby twój zakres funkcją potrzebną tylko w tym jednym miejscu i zakłóciło naturalny przepływ i czytanie twojego kodu (kolega musiałby zostawić ten kod, aby znaleźć twoją funkcja, aby zrozumieć, co się dzieje).
Zwracanie anonimowej funkcji z innej funkcji
Czasami warto zwrócić funkcję jako wynik innej funkcji. Na przykład:
var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );
function getHashFunction( algorithm ){
if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };
}
Natychmiastowe wywołanie funkcji anonimowej
W przeciwieństwie do wielu innych języków, zakres w JavaScript jest zależny od poziomu funkcji, a nie poziomu bloku. (Zobacz Określanie zakresu funkcji ). W niektórych przypadkach konieczne jest jednak utworzenie nowego zakresu. Na przykład często dodaje się nowy zakres, dodając kod za pomocą <script>
, zamiast pozwalać na definiowanie nazw zmiennych w zasięgu globalnym (co stwarza ryzyko kolizji innych skryptów z nazwami zmiennych). Częstą metodą radzenia sobie z tą sytuacją jest zdefiniowanie nowej funkcji anonimowej, a następnie natychmiastowe jej wywołanie, bezpieczne ukrywanie zmiennych w zakresie funkcji anonimowej i bez udostępniania kodu osobom trzecim za pośrednictwem wyciekającej nazwy funkcji. Na przykład:
<!-- My Script -->
<script>
function initialize(){
// foo is safely hidden within initialize, but...
var foo = '';
}
// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>
<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>
Anonimowe funkcje samo-referencyjne
Czasami użyteczne jest, aby anonimowa funkcja mogła odnosić się do siebie. Na przykład funkcja może wymagać rekurencyjnego wywoływania się lub dodawania do siebie właściwości. Jeśli funkcja jest anonimowa, może to być bardzo trudne, ponieważ wymaga znajomości zmiennej, do której funkcja została przypisana. To mniej niż idealne rozwiązanie:
var foo = function(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) foo(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Bad.
Chodziło tu o to, aby anonimowa funkcja wywoływała się rekurencyjnie, ale gdy zmienia się wartość foo, pojawia się potencjalnie trudny do wyśledzenia błąd.
Zamiast tego możemy nadać funkcji anonimowej odniesienie do siebie, nadając jej prywatną nazwę, taką jak:
var foo = function myself(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) myself(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Whassup?
Zwróć uwagę, że nazwa funkcji jest ograniczona do siebie. Nazwa nie wyciekła do zewnętrznego zakresu:
myself(false); // ReferenceError: myself is not defined
Ta technika jest szczególnie przydatna w przypadku rekurencyjnych funkcji anonimowych jako parametrów wywołania zwrotnego:
// Calculate the fibonacci value for each number in an array:
var fib = false,
result = [1,2,3,4,5,6,7,8].map(
function fib(n){
return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
});
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)
Natychmiast wywołane wyrażenia funkcyjne
Czasami nie chcesz, aby twoja funkcja była dostępna / przechowywana jako zmienna. Możesz utworzyć Wyrażenie funkcji natychmiast wywołanej (w skrócie IIFE). Są to w gruncie rzeczy anonimowe funkcje . Mają dostęp do otaczającego zakresu, ale sama funkcja i wszelkie zmienne wewnętrzne będą niedostępne z zewnątrz. Ważną rzeczą do zapamiętania na temat IIFE jest to, że nawet jeśli nazwiesz swoją funkcję, IIFE nie są podnoszone tak jak standardowe funkcje i nie można ich wywołać przez nazwę funkcji, którą są zadeklarowane.
(function() {
alert("I've run - but can't be run again because I'm immediately invoked at runtime,
leaving behind only the result I generate");
}());
To kolejny sposób na napisanie IIFE. Zauważ, że nawias zamykający przed średnikiem został przesunięty i umieszczony tuż za nawiasem zamykającym:
(function() {
alert("This is IIFE too.");
})();
Możesz łatwo przekazać parametry do IIFE:
(function(message) {
alert(message);
}("Hello World!"));
Dodatkowo możesz zwrócić wartości do otaczającego zakresu:
var example = (function() {
return 42;
}());
console.log(example); // => 42
W razie potrzeby można nazwać IIFE. Chociaż rzadziej widziany, ten wzorzec ma kilka zalet, takich jak zapewnienie odwołania, które można wykorzystać do rekurencji i może uprościć debugowanie, ponieważ nazwa jest zawarta w stosie wywołań.
(function namedIIFE() {
throw error; // We can now see the error thrown in 'namedIIFE()'
}());
Podczas gdy zawijanie funkcji w nawiasach jest najczęstszym sposobem oznaczenia w parserze Javascript, aby oczekiwał wyrażenia, w miejscach, w których wyrażenie jest już oczekiwane, notację można uczynić bardziej zwięzłą:
var a = function() { return 42 }();
console.log(a) // => 42
Wersja strzałkowa natychmiast wywołanej funkcji:
(() => console.log("Hello!"))(); // => Hello!
Określanie zakresu funkcji
Po zdefiniowaniu funkcji tworzy zasięg .
Wszystko zdefiniowane w funkcji nie jest dostępne przez kod poza funkcją. Tylko kod w tym zakresie może widzieć jednostki zdefiniowane w zakresie.
function foo() {
var a = 'hello';
console.log(a); // => 'hello'
}
console.log(a); // reference error
Funkcje zagnieżdżone są możliwe w JavaScript i obowiązują te same zasady.
function foo() {
var a = 'hello';
function bar() {
var b = 'world';
console.log(a); // => 'hello'
console.log(b); // => 'world'
}
console.log(a); // => 'hello'
console.log(b); // reference error
}
console.log(a); // reference error
console.log(b); // reference error
Gdy JavaScript próbuje rozwiązać odwołanie lub zmienną, zaczyna szukać go w bieżącym zakresie. Jeśli nie może znaleźć tej deklaracji w bieżącym zakresie, wspina się o jeden zakres, aby go wyszukać. Ten proces powtarza się, dopóki deklaracja nie zostanie znaleziona. Jeśli parser JavaScript osiągnie zasięg globalny i nadal nie może znaleźć odwołania, zostanie zgłoszony błąd odwołania.
var a = 'hello';
function foo() {
var b = 'world';
function bar() {
var c = '!!';
console.log(a); // => 'hello'
console.log(b); // => 'world'
console.log(c); // => '!!'
console.log(d); // reference error
}
}
To zachowanie podczas wspinaczki może również oznaczać, że jedno odniesienie może „ukrywać się” nad podobnie nazwanym odniesieniem w zakresie zewnętrznym, ponieważ jest ono widoczne jako pierwsze.
var a = 'hello';
function foo() {
var a = 'world';
function bar() {
console.log(a); // => 'world'
}
}
Sposób, w jaki JavaScript rozwiązuje zakres, dotyczy również słowa kluczowego const
. Zadeklarowanie zmiennej za pomocą słowa kluczowego const
oznacza, że nie można ponownie przypisać wartości, ale zadeklarowanie jej w funkcji spowoduje utworzenie nowego zakresu, a wraz z nią nowej zmiennej.
function foo() {
const a = true;
function bar() {
const a = false; // different variable
console.log(a); // false
}
const a = false; // SyntaxError
a = false; // TypeError
console.log(a); // true
}
Jednak funkcje nie są jedynymi blokami, które tworzą zakres (jeśli używasz let
lub const
). Deklaracje let
i const
mają zakres instrukcji najbliższego bloku. Tutaj znajdziesz bardziej szczegółowy opis.
Wiązanie „tego” i argumentów
Kiedy odwołujesz się do metody (właściwość, która jest funkcją) w JavaScript, zwykle nie zapamiętuje obiektu, do którego był pierwotnie dołączony. Jeśli potrzeby metoda odnosi się do tego obiektu, jak this
, że nie będzie w stanie, i nazywając ją prawdopodobnie spowoduje katastrofę.
Za pomocą metody .bind()
funkcji można utworzyć opakowanie zawierające wartość this
i dowolnej liczby wiodących argumentów.
var monitor = {
threshold: 5,
check: function(value) {
if (value > this.threshold) {
this.display("Value is too high!");
}
},
display(message) {
alert(message);
}
};
monitor.check(7); // The value of `this` is implied by the method call syntax.
var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false
var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.
var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.
Gdy nie jest w trybie ścisłym, funkcja używa globalnego obiektu ( window
w przeglądarce) w this
, chyba że funkcja jest wywoływana jako metoda, powiązana lub wywoływana za pomocą metody .call
.
window.x = 12;
function example() {
return this.x;
}
console.log(example()); // 12
W trybie ścisłym this
jest undefined
domyślnie
window.x = 12;
function example() {
"use strict";
return this.x;
}
console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
Bind Operator
Operator wiązania podwójnego dwukropka może być użyty jako skrócona składnia dla wyjaśnionej powyżej koncepcji:
var log = console.log.bind(console); // long version
const log = ::console.log; // short version
foo.bar.call(foo); // long version
foo::bar(); // short version
foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version
foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version
Ta składnia pozwala pisać normalnie, nie martwiąc się o wiązanie this
wszędzie.
Wiązanie funkcji konsoli ze zmiennymi
var log = console.log.bind(console);
Stosowanie:
log('one', '2', 3, [4], {5: 5});
Wynik:
one 2 3 [4] Object {5: 5}
Dlaczego chcesz to zrobić?
Jednym z przypadków użycia może być sytuacja, gdy masz niestandardowy program rejestrujący i chcesz zdecydować o czasie wykonywania, którego użyć.
var logger = require('appLogger');
var log = logToServer ? logger.log : console.log.bind(console);
Argumenty funkcji, obiekt „argumenty”, parametry spoczynku i rozkładania
Funkcje mogą przyjmować dane wejściowe w postaci zmiennych, które mogą być używane i przypisywane we własnym zakresie. Poniższa funkcja przyjmuje dwie wartości liczbowe i zwraca ich sumę:
function addition (argument1, argument2){
return argument1 + argument2;
}
console.log(addition(2, 3)); // -> 5
obiekt arguments
Obiekt arguments
zawiera wszystkie parametry funkcji, które zawierają wartość inną niż domyślna . Można go również użyć, nawet jeśli parametry nie są wyraźnie zadeklarowane:
(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]
Chociaż podczas drukowania arguments
dane wyjściowe przypominają tablicę, w rzeczywistości jest to obiekt:
(function() { console.log(typeof arguments) })(); // -> object
Parametry odpoczynku: function (...parm) {}
W ES6 składnia ...
zastosowana w deklaracji parametrów funkcji przekształca zmienną po jej prawej stronie w pojedynczy obiekt zawierający wszystkie pozostałe parametry podane po zadeklarowanych. Pozwala to na wywołanie funkcji z nieograniczoną liczbą argumentów, które staną się częścią tej zmiennej:
(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123
Parametry rozprzestrzeniania: function_name(...varb);
W ES6 składnia ...
może być również używana podczas wywoływania funkcji poprzez umieszczenie obiektu / zmiennej po jej prawej stronie. Dzięki temu elementy tego obiektu mogą być przekazywane do tej funkcji jako pojedynczy obiekt:
let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42
Nazwane funkcje
Funkcje mogą być nazwane lub nienazwane ( funkcje anonimowe ):
var namedSum = function sum (a, b) { // named
return a + b;
}
var anonSum = function (a, b) { // anonymous
return a + b;
}
namedSum(1, 3);
anonSum(1, 3);
4
4
Ale ich nazwy są prywatne według własnego zakresu:
var sumTwoNumbers = function sum (a, b) {
return a + b;
}
sum(1, 3);
Uncaught ReferenceError: suma nie jest zdefiniowana
Funkcje nazwane różnią się od funkcji anonimowych w wielu scenariuszach:
- Podczas debugowania nazwa funkcji pojawi się w śladzie błędu / stosu
- Funkcje nazwane są podnoszone, podczas gdy funkcje anonimowe nie są
- Funkcje nazwane i funkcje anonimowe zachowują się inaczej podczas obsługi rekurencji
- W zależności od wersji ECMAScript funkcje nazwane i anonimowe mogą traktować właściwość
name
funkcji inaczej
Funkcje nazwane są podnoszone
Podczas korzystania z funkcji anonimowej funkcja może być wywołana tylko po wierszu deklaracji, natomiast funkcja nazwana może być wywołana przed deklaracją. Rozważać
foo();
var foo = function () { // using an anonymous function
console.log('bar');
}
Uncaught TypeError: foo nie jest funkcją
foo();
function foo () { // using a named function
console.log('bar');
}
bar
Nazwane funkcje w scenariuszu rekurencyjnym
Funkcję rekurencyjną można zdefiniować jako:
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
//you could call 'say' directly,
//but this way just illustrates the example
var sayHelloTimes = say;
sayHelloTimes(2);
Cześć!
Cześć!
Co się stanie, jeśli gdzieś w kodzie zostanie ponownie zdefiniowane pierwotne powiązanie funkcji?
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Cześć!
Uncaught TypeError: say nie jest funkcją
Można to rozwiązać za pomocą nazwanej funkcji
// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it uses the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Cześć!
Cześć!
Jako bonus, nazwanej funkcji nie można ustawić na undefined
, nawet od wewnątrz:
var say = function say (times) {
// this does nothing
say = undefined;
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it's using the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Cześć!
Cześć!
Właściwość name
funkcji
Przed ES6, nazwane funkcje miał ich name
właściwości ustawione na nazw funkcji i funkcje anonimowe miał ich name
właściwości ustawiony na pusty ciąg.
var foo = function () {}
console.log(foo.name); // outputs ''
function foo () {}
console.log(foo.name); // outputs 'foo'
Funkcje nazwane i nienazwane po ES6 ustawiają swoje właściwości name
:
var foo = function () {}
console.log(foo.name); // outputs 'foo'
function foo () {}
console.log(foo.name); // outputs 'foo'
var foo = function bar () {}
console.log(foo.name); // outputs 'bar'
Funkcja rekurencyjna
Funkcja rekurencyjna to po prostu funkcja, która by się wywołała.
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Powyższa funkcja pokazuje podstawowy przykład wykonania funkcji rekurencyjnej w celu zwrócenia silni.
Innym przykładem byłoby pobranie sumy liczb parzystych w tablicy.
function countEvenNumbers (arr) {
// Sentinel value. Recursion stops on empty array.
if (arr.length < 1) {
return 0;
}
// The shift() method removes the first element from an array
// and returns that element. This method changes the length of the array.
var value = arr.shift();
// `value % 2 === 0` tests if the number is even or odd
// If it's even we add one to the result of counting the remainder of
// the array. If it's odd, we add zero to it.
return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}
Ważne jest, aby takie funkcje dokonywały pewnego rodzaju kontroli wartości wartownika, aby uniknąć nieskończonych pętli. W pierwszym przykładzie powyżej, gdy n
jest mniejsze lub równe 1, rekurencja zatrzymuje się, umożliwiając powrót wyniku każdego wywołania z powrotem na stos wywołań.
Curry
Curry to przekształcenie funkcji n
arity lub argumentów w sekwencję n
funkcji, która przyjmuje tylko jeden argument.
Przypadki użycia: Gdy wartości niektórych argumentów są dostępne przed innymi, możesz użyć curry, aby rozłożyć funkcję na szereg funkcji, które kończą pracę etapami, gdy nadejdzie każda wartość. Może to być przydatne:
- Gdy wartość argumentu prawie nigdy się nie zmienia (np. Współczynnik konwersji), ale musisz zachować elastyczność ustawiania tej wartości (zamiast zakodować ją na stałe jako stałą).
- Gdy wynik funkcji curry jest przydatny przed uruchomieniem innych funkcji curry.
- Aby sprawdzić przybycie funkcji w określonej kolejności.
Na przykład objętość prostokątnego pryzmatu można wytłumaczyć funkcją trzech czynników: długości ( l
), szerokości ( w
) i wysokości ( h
):
var prism = function(l, w, h) {
return l * w * h;
}
Curry wersja tej funkcji wyglądałaby następująco:
function prism(l) {
return function(w) {
return function(h) {
return l * w * h;
}
}
}
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;
Możesz wywołać tę sekwencję funkcji za pomocą prism(2)(3)(5)
, co powinno dać wynik 30.
Bez dodatkowej maszynerii (jak w bibliotekach) curry ma ograniczoną elastyczność syntaktyczną w JavaScript (ES 5/6) z powodu braku wartości symboli zastępczych; dlatego, chociaż możesz użyć var a = prism(2)(3)
aby utworzyć częściowo zastosowaną funkcję , nie możesz użyć prism()(3)(5)
.
Korzystanie ze sprawozdania zwrotnego
Instrukcja return może być użytecznym sposobem tworzenia danych wyjściowych dla funkcji. Instrukcja return jest szczególnie przydatna, jeśli nie wiesz, w jakim kontekście funkcja będzie jeszcze używana.
//An example function that will take a string as input and return
//the first character of the string.
function firstChar (stringIn){
return stringIn.charAt(0);
}
Teraz, aby użyć tej funkcji, musisz umieścić ją w miejscu zmiennej w innym miejscu w kodzie:
Wykorzystanie wyniku funkcji jako argumentu dla innej funkcji:
console.log(firstChar("Hello world"));
Dane wyjściowe konsoli będą:
> H
Instrukcja return kończy funkcję
Jeśli zmodyfikujemy funkcję na początku, możemy wykazać, że instrukcja return kończy funkcję.
function firstChar (stringIn){
console.log("The first action of the first char function");
return stringIn.charAt(0);
console.log("The last action of the first char function");
}
Uruchomienie tej funkcji w ten sposób będzie wyglądać następująco:
console.log(firstChar("JS"));
Dane wyjściowe konsoli:
> The first action of the first char function
> J
Komunikat nie zostanie wydrukowany po instrukcji return, ponieważ funkcja została zakończona.
Instrukcja return obejmująca wiele wierszy:
W JavaScript można zwykle podzielić wiersz kodu na wiele wierszy w celu zapewnienia czytelności lub organizacji. To jest poprawny JavaScript:
var
name = "bob",
age = 18;
Gdy JavaScript widzi niekompletną instrukcję, taką jak var
, przechodzi do następnego wiersza, aby się uzupełnić. Jeśli jednak popełnisz ten sam błąd przy instrukcji return
, nie dostaniesz tego, czego się spodziewałeś.
return
"Hi, my name is "+ name + ". " +
"I'm "+ age + " years old.";
Ten kod zwróci undefined
ponieważ return
sam w sobie jest kompletną instrukcją w Javascripcie, więc nie będzie szukał następnego wiersza w celu uzupełnienia. Jeśli chcesz podzielić instrukcję return
na wiele wierszy, umieść wartość obok zwracanej wartości, zanim ją podzielisz.
return "Hi, my name is " + name + ". " +
"I'm " + age + " years old.";
Przekazywanie argumentów przez referencję lub wartość
W JavaScript wszystkie argumenty są przekazywane przez wartość. Gdy funkcja przypisuje nową wartość do zmiennej argumentu, zmiana ta nie będzie widoczna dla obiektu wywołującego:
var obj = {a: 2};
function myfunc(arg){
arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2
Jednakże, wprowadzenie zmian (zagnieżdżonych) Właściwości tych argumentów, będą widoczne dla rozmówcy:
var obj = {a: 2};
function myfunc(arg){
arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5
Można to postrzegać jako wywołanie przez odwołanie : chociaż funkcja nie może zmienić obiektu dzwoniącego poprzez przypisanie mu nowej wartości, może mutować obiekt dzwoniącego.
Ponieważ argumenty o wartościach pierwotnych, takie jak liczby lub łańcuchy, są niezmienne, nie ma możliwości, aby funkcja ich zmutowała:
var s = 'say';
function myfunc(arg){
arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'
Gdy funkcja chce mutować obiekt przekazany jako argument, ale nie chce faktycznie mutować obiektu wywołującego, zmienna argumentu powinna zostać ponownie przypisana:
var obj = {a: 2, b: 3};
function myfunc(arg){
arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2
Jako alternatywa dla mutacji w miejscu argumentu, funkcje mogą utworzyć nową wartość na podstawie argumentu i zwrócić ją. Osoba wywołująca może następnie przypisać ją, nawet do oryginalnej zmiennej, która została przekazana jako argument:
var a = 2;
function myfunc(arg){
arg++;
return arg;
}
a = myfunc(a);
console.log(obj.a); // 3
Zadzwoń i złóż wniosek
Funkcje mają dwie wbudowane metody, które pozwalają programiście podać argumenty i this
zmienną inaczej: call
i apply
.
Jest to przydatne, ponieważ funkcje działające na jednym obiekcie (obiekcie, którego są własnością) mogą zostać zmienione tak, aby działały na innym kompatybilnym obiekcie. Dodatkowo, argumenty mogą być podawane w jednym ujęciu jako tablice, podobnie jak operator rozkładania ( ...
) w ES6.
let obj = {
a: 1,
b: 2,
set: function (a, b) {
this.a = a;
this.b = b;
}
};
obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used
console.log(obj); // prints { a: 3, b: 5 }
let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array
console.log(myObj); // prints { a: 3, b: 5 }
ECMAScript 5 wprowadził inną metodę o nazwie bind()
oprócz call()
i apply()
aby jawnie ustawić this
wartość funkcji na konkretny obiekt.
Zachowuje się zupełnie inaczej niż pozostałe dwa. Pierwszym argumentem bind()
jest this
wartość nowej funkcji. Wszystkie pozostałe argumenty reprezentują nazwane parametry, które powinny zostać na stałe ustawione w nowej funkcji.
function showName(label) {
console.log(label + ":" + this.name);
}
var student1 = {
name: "Ravi"
};
var student2 = {
name: "Vinod"
};
// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"
// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"
// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"
Parametry domyślne
Przed ECMAScript 2015 (ES6) wartość domyślną parametru można było przypisać w następujący sposób:
function printMsg(msg) {
msg = typeof msg !== 'undefined' ? // if a value was provided
msg : // then, use that value in the reassignemnt
'Default value for msg.'; // else, assign a default value
console.log(msg);
}
ES6 zapewnił nową składnię, w której opisany powyżej warunek i zmiana przypisania nie jest już konieczne:
function printMsg(msg='Default value for msg.') {
console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"
Pokazuje to również, że jeśli brakuje parametru podczas wywoływania funkcji, jego wartość jest zachowywana jako undefined
, co można potwierdzić, podając ją jawnie w poniższym przykładzie (za pomocą funkcji strzałki ):
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);
param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"
param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"
Funkcje / zmienne jako wartości domyślne i ponowne użycie parametrów
Domyślne wartości parametrów nie są ograniczone do liczb, ciągów znaków lub prostych obiektów. Funkcję można również ustawić jako wartość domyślną callback = function(){}
:
function foo(callback = function(){ console.log('default'); }) {
callback();
}
foo(function (){
console.log('custom');
});
// custom
foo();
//default
Istnieją pewne cechy operacji, które można wykonać za pomocą wartości domyślnych:
- Poprzednio zadeklarowany parametr może być ponownie użyty jako wartość domyślna dla wartości nadchodzących parametrów.
- Operacje wstawiane są dozwolone podczas przypisywania wartości domyślnej do parametru.
- Zmienne istniejące w tym samym zakresie deklarowanej funkcji mogą być użyte w jej wartościach domyślnych.
- Funkcje można wywoływać w celu nadania wartości zwracanej wartości domyślnej.
let zero = 0;
function multiply(x) { return x * 2;}
function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
console.log((a + b + c), d);
}
add(1); // 4, 4
add(3); // 12, 12
add(2, 7); // 18, 18
add(1, 2, 5); // 8, 10
add(1, 2, 5, 10); // 8, 20
Ponowne użycie wartości zwracanej przez funkcję w wartości domyślnej nowego wywołania:
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
array.push(value);
return array;
}
add(5); // [5]
add(6); // [6], not [5, 6]
add(6, add(5)); // [5, 6]
arguments
wartość i długość, gdy brakuje parametrów w wywołaniu
Obiekt tablicy arguments
zachowuje tylko parametry, których wartości nie są domyślne, tj. Te, które są jawnie podane podczas wywoływania funkcji:
function foo(a = 1, b = a + 1) {
console.info(arguments.length, arguments);
console.log(a,b);
}
foo(); // info: 0 >> [] | log: 1, 2
foo(4); // info: 1 >> [4] | log: 4, 5
foo(5, 6); // info: 2 >> [5, 6] | log: 5, 6
Funkcje o nieznanej liczbie argumentów (funkcje variadic)
Aby utworzyć funkcję, która akceptuje nieokreśloną liczbę argumentów, istnieją dwie metody w zależności od środowiska.
Za każdym razem, gdy wywoływana jest funkcja, w jej zasięgu znajduje się obiekt argumentów podobny do tablicy, zawierający wszystkie argumenty przekazane do funkcji. Indeksowanie lub iteracja nad tym zapewni na przykład dostęp do argumentów
function logSomeThings() {
for (var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
}
}
logSomeThings('hello', 'world');
// logs "hello"
// logs "world"
Zauważ, że możesz w razie potrzeby przekonwertować arguments
na rzeczywistą macierz; patrz: Konwertowanie obiektów podobnych do macierzy na tablice
W ES6 funkcję można zadeklarować ostatnim parametrem za pomocą operatora rest ( ...
). To tworzy tablicę, która przechowuje argumenty od tego momentu
function personLogsSomeThings(person, ...msg) {
msg.forEach(arg => {
console.log(person, 'says', arg);
});
}
personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"
Funkcje można również wywoływać w podobny sposób, przy użyciu składni rozszerzonej
const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]
logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]
Składnia może być użyty, aby dodać dowolną liczbę argumentów każdej pozycji i może być stosowany z dowolnym iterowalny ( apply
akceptuje jedynie tablica przedmiotów podobnych).
const logArguments = (...args) => console.log(args)
function* generateNumbers() {
yield 6
yield 5
yield 4
}
logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]
Uzyskaj nazwę obiektu funkcji
ES6 :
myFunction.name
Objaśnienie dotyczące MDN . Od 2015 roku działa w nodejs i wszystkich głównych przeglądarkach oprócz IE.
ES5 :
Jeśli masz odwołanie do funkcji, możesz:
function functionName( func )
{
// Match:
// - ^ the beginning of the string
// - function the word 'function'
// - \s+ at least some white space
// - ([\w\$]+) capture one or more valid JavaScript identifier characters
// - \( followed by an opening brace
//
var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
return result ? result[1] : ''
}
Częściowe zastosowanie
Podobnie jak curry, częściowa aplikacja służy do zmniejszenia liczby argumentów przekazywanych do funkcji. W przeciwieństwie do curry liczba nie musi być mniejsza o jeden.
Przykład:
Ta funkcja ...
function multiplyThenAdd(a, b, c) {
return a * b + c;
}
... można użyć do utworzenia innej funkcji, która zawsze będzie mnożona przez 2, a następnie doda 10 do przekazywanej wartości;
function reversedMultiplyThenAdd(c, b, a) {
return a * b + c;
}
function factory(b, c) {
return reversedMultiplyThenAdd.bind(null, c, b);
}
var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30
Część „aplikacji” częściowej aplikacji oznacza po prostu ustalenie parametrów funkcji.
Skład funkcji
Składanie wielu funkcji w jedną jest powszechną praktyką programowania funkcjonalnego;
Kompozycja tworzy potok, przez który nasze dane będą przesyłane i modyfikowane, po prostu pracując nad kompozycją funkcji (podobnie jak łączenie fragmentów ścieżki razem) ...
zaczynasz od kilku funkcji odpowiedzialności:
const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
const sign = x => x + ',\nmade with love';
i łatwo utwórz ścieżkę transformacji:
const formatText = compose(capitalize, sign);
formatText('this is an example')
//This is an example,
//made with love
NB Kompozycję uzyskuje się za pomocą funkcji użyteczności zwanej zwykle compose
jak w naszym przykładzie.
Implementacja compose
jest obecna w wielu bibliotekach narzędzi JavaScript ( lodash , rambda itp.), Ale możesz też zacząć od prostej implementacji, takiej jak:
const compose = (...funs) =>
x =>
funs.reduce((ac, f) => f(ac), x);