Sök…
Introduktion
Funktioner i JavaScript ger organiserad, återanvändbar kod för att utföra en uppsättning åtgärder. Funktioner förenklar kodningsprocessen, förhindrar redundant logik och gör koden lättare att följa. Det här ämnet beskriver deklarationen och användningen av funktioner, argument, parametrar, returrätt och omfattning i JavaScript.
Syntax
funktionsexempel (x) {return x}
var exempel = funktion (x) {return x}
(funktion () {...}) (); // Omedelbart åberopad funktionskontroll (IIFE)
var instans = nytt exempel (x);
metoder
fn.apply (valueForThis [, arrayOfArgs])
fn.bind (valueForThis [, arg1 [, arg2, ...]])
fn.call (valueForThis [, arg1 [, arg2, ...]])
ES2015 + (ES6 +):
const exempel = x => {return x}; // Pilfunktionen uttrycklig återgång
const exempel = x => x; // Pilfunktion implicit retur
const exempel = (x, y, z) => {...} // Pilfunktion flera argument
(() => {...}) (); // IIFE med hjälp av en pilfunktion
Anmärkningar
För information om pilfunktioner, se dokumentationen för pilfunktioner .
Funktioner som en variabel
En normal funktionsdeklaration ser ut så här:
function foo(){
}
En funktion som definieras som denna är tillgänglig var som helst inom dess sammanhang med dess namn. Men ibland kan det vara användbart att behandla funktionsreferenser som objektreferenser. Till exempel kan du tilldela ett objekt till en variabel baserat på vissa uppsättningar av villkor och sedan senare hämta en egenskap från det ena eller det andra objektet:
var name = 'Cameron';
var spouse;
if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };
var spouseName = spouse.name;
I JavaScript kan du göra samma sak med funktioner:
// Example 1
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };
hash('Fred');
I exemplet ovan är hash
en normal variabel. Det tilldelas en referens till en funktion, varefter funktionen som den refererar kan åberopas med parenteser, precis som en normal funktionsdeklaration.
Exemplet ovan hänvisar till anonyma funktioner ... funktioner som inte har sitt eget namn. Du kan också använda variabler för att hänvisa till namngivna funktioner. Exemplet ovan kan skrivas om så:
// 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){
// ...
}
Du kan också tilldela funktionsreferenser från objektegenskaper:
// 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');
Du kan tilldela referensen till en funktion som innehas av en variabel till en annan genom att utelämna parenteserna. Detta kan resultera i ett lätt att göra misstag: att försöka tilldela ett funktions returvärde till en annan variabel, men av misstag tilldela referensen till funktionen.
// 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;
}
En hänvisning till en funktion är som alla andra värden. Som du har sett kan en referens tilldelas en variabel, och variabelns referensvärde kan sedan tilldelas andra variabler. Du kan överföra referenser till funktioner som alla andra värden, inklusive att skicka en referens till en funktion som returvärde för en annan funktion. Till exempel:
// 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){
// ...
}
Du behöver inte tilldela en funktionsreferens till en variabel för att åberopa den. Det här exemplet, som bygger upp exempel 5, kommer att ringa getHashingFunction och sedan omedelbart åberopa den returnerade funktionen och överföra dess returvärde till hashedValue.
// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );
En anmärkning om hissning
Tänk på att, till skillnad från normala funktionsdeklarationer, variabler som referensfunktioner inte "hejs". I exempel 2 sha1Hash
funktionerna md5Hash
och sha1Hash
längst ner i skriptet, men är tillgängliga överallt omedelbart. Oavsett var du definierar en funktion, "tolkar" tolken den upp till dess omfattning och gör den omedelbart tillgänglig. Detta är inte fallet för variabla definitioner, så kod som följande kommer att bryta:
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(){};
Anonym funktion
Definiera en anonym funktion
När en funktion definieras, ger du den ofta ett namn och åkallar den sedan med det namnet, som så:
foo();
function foo(){
// ...
}
När du definierar en funktion på detta sätt lagrar Javascript runtime din funktion i minnet och skapar sedan en referens till den funktionen med det namn du har tilldelat den. Namnet är då tillgängligt inom det nuvarande räckvidden. Detta kan vara ett mycket bekvämt sätt att skapa en funktion, men Javascript kräver inte att du tilldelar ett namn till en funktion. Följande är också helt lagligt:
function() {
// ...
}
När en funktion definieras utan namn, är den känd som en anonym funktion. Funktionen lagras i minnet, men körtiden skapar inte automatiskt en referens till den för dig. Vid första anblicken kan det se ut som om något sådant inte skulle ha någon nytta, men det finns flera scenarier där anonyma funktioner är mycket praktiska.
Tilldela en anonym funktion till en variabel
En mycket vanlig användning av anonyma funktioner är att tilldela dem till en variabel:
var foo = function(){ /*...*/ };
foo();
Denna användning av anonyma funktioner behandlas mer detaljerat i funktioner som en variabel
Tillhandahålla en anonym funktion som en parameter till en annan funktion
Vissa funktioner kan acceptera en hänvisning till en funktion som en parameter. Dessa kallas ibland "beroendeinjektioner" eller "återuppringningar", eftersom det tillåter den funktion som ditt samtal "ringer tillbaka" till din kod, vilket ger dig en möjlighet att ändra hur den kallade funktionen beter sig. Arrayobjektets kartfunktion till exempel tillåter dig att iterera över varje element i en matris och sedan bygga en ny matris genom att tillämpa en transformationsfunktion på varje element.
var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]
Det skulle vara tråkigt, slarvigt och onödigt att skapa en namngiven funktion, som skulle skruva ditt omfattning med en funktion som bara behövs på denna ena plats och bryta det naturliga flödet och läsningen av din kod (en kollega måste lämna den här koden för att hitta din funktion för att förstå vad som händer).
Återvänd en anonym funktion från en annan funktion
Ibland är det användbart att returnera en funktion som resultat av en annan funktion. Till exempel:
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 ){ /*...*/ };
}
Omedelbart åberopa en anonym funktion
Till skillnad från många andra språk är scoping i Javascript funktionsnivå, inte blocknivå. (Se Funktionsomfattning ). I vissa fall är det dock nödvändigt att skapa ett nytt omfång. Till exempel är det vanligt att skapa ett nytt omfång när du lägger till kod via en <script>
-tagg, snarare än att låta variabla namn definieras i det globala omfånget (vilket riskerar att andra skript kolliderar med dina variabla namn). En vanlig metod för att hantera denna situation är att definiera en ny anonym funktion och sedan omedelbart åberopa den, på ett säkert sätt dölja dig variabler inom ramen för den anonyma funktionen och utan att göra din kod tillgänglig för tredje parter via ett läckt funktionsnamn. Till exempel:
<!-- 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>
Anonyma funktioner för självreferenser
Ibland är det användbart för en anonym funktion att kunna hänvisa till sig själv. Till exempel kan funktionen behöva rekursivt kalla sig själv eller lägga till egenskaper till sig själv. Om funktionen är anonym kan detta dock vara mycket svårt eftersom det kräver kunskap om variabeln som funktionen har tilldelats. Detta är den mindre än idealiska lösningen:
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.
Avsikten här var att den anonyma funktionen skulle rekursivt kalla sig själv, men när värdet av foo förändras slutar du med ett potentiellt svårt att spåra fel.
I stället kan vi ge den anonyma funktionen en referens till sig själv genom att ge den ett privat namn, så:
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?
Observera att funktionsnamnet skopas till sig själv. Namnet har inte läckt ut i det yttre räckvidden:
myself(false); // ReferenceError: myself is not defined
Denna teknik är särskilt användbar när man hanterar rekursiva anonyma funktioner som återuppringningsparametrar:
// 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)
Omedelbart påkallade funktionsuttryck
Ibland vill du inte ha din funktion tillgänglig / lagrad som en variabel. Du kan skapa ett omedelbart påkallat funktionsuttryck (IIFE för kort). Dessa är i huvudsak självutförande av anonyma funktioner . De har tillgång till det omgivande omfånget, men själva funktionen och eventuella interna variabler är oåtkomliga från utsidan. En viktig sak att notera om IIFE är att även om du namnger din funktion så hejs IIFE inte som standardfunktioner är och kan inte kallas med det funktionsnamn som de deklareras med.
(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");
}());
Detta är ett annat sätt att skriva IIFE. Lägg märke till att den stängande parentesen före semikolon flyttades och placerades direkt efter den stängande krullningsfästet:
(function() {
alert("This is IIFE too.");
})();
Du kan enkelt skicka parametrar till en IIFE:
(function(message) {
alert(message);
}("Hello World!"));
Dessutom kan du returnera värden till det omgivande omfånget:
var example = (function() {
return 42;
}());
console.log(example); // => 42
Vid behov är det möjligt att namnge en IIFE. Även om det ses mindre ofta har detta mönster flera fördelar, såsom att tillhandahålla en referens som kan användas för en rekursion och kan göra felsökning enklare eftersom namnet ingår i samtalstacken.
(function namedIIFE() {
throw error; // We can now see the error thrown in 'namedIIFE()'
}());
Medan inslagning av en funktion inom parentes är det vanligaste sättet att beteckna till Javascript-parser att förvänta sig ett uttryck, på platser där ett uttryck redan förväntas, kan notationen göras mer kortfattad:
var a = function() { return 42 }();
console.log(a) // => 42
Pilversion av omedelbart åberopad funktion:
(() => console.log("Hello!"))(); // => Hello!
Funktionsomfång
När du definierar en funktion skapar den en räckvidd .
Allt som definieras i funktionen är inte tillgängligt med kod utanför funktionen. Endast kod inom detta räckvidd kan se de enheter som definieras inom ramen.
function foo() {
var a = 'hello';
console.log(a); // => 'hello'
}
console.log(a); // reference error
Kapslade funktioner är möjliga i JavaScript och samma regler gäller.
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
När JavaScript försöker lösa en referens eller en variabel börjar den leta efter den i det aktuella omfånget. Om den inte kan hitta den deklarationen i det nuvarande räckvidden, klättrar den upp en räckvidd för att leta efter den. Denna process upprepas tills deklarationen har hittats. Om JavaScript-parsern når den globala räckvidden och fortfarande inte kan hitta referensen kastas ett referensfel.
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
}
}
Detta klättringsbeteende kan också innebära att en referens kan "skugga" över en liknande namngivna referens i det yttre omfånget eftersom den först sett.
var a = 'hello';
function foo() {
var a = 'world';
function bar() {
console.log(a); // => 'world'
}
}
Hur JavaScript löser scoping gäller också för nyckelordet const
. Att förklara en variabel med nyckelordet const
innebär att du inte får tilldela värdet, men om du förklarar det i en funktion skapas ett nytt omfång och därmed en ny variabel.
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
}
Men funktioner är inte de enda blocken som skapar ett omfattning (om du använder let
eller const
). let
och const
deklarationer har en räckvidd för det närmaste blockförklaringen. Se här för en mer detaljerad beskrivning.
Bindande "detta" och argument
När du hänvisar till en metod (en egenskap som är en funktion) i JavaScript, kommer den inte ihåg det objekt som det ursprungligen var kopplat till. Om metoden behöver hänvisa till objektet eftersom this
inte kommer att kunna, och kalla det kommer troligen att orsaka en krasch.
Du kan använda .bind()
på en funktion för att skapa en omslag som innehåller värdet på this
och valfritt antal ledande argument.
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.
När den inte är i strikt läge använder en funktion det globala objektet ( window
i webbläsaren) som this
, såvida inte funktionen kallas som en metod, bunden eller kallas med metoden .call
syntax.
window.x = 12;
function example() {
return this.x;
}
console.log(example()); // 12
I strikt läge this
är undefined
som standard
window.x = 12;
function example() {
"use strict";
return this.x;
}
console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
Bind operatör
Operatören med dubbel kolonbindning kan användas som en förkortad syntax för konceptet som förklaras ovan:
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
Denna syntax låter dig skriva normalt, utan att oroa dig för att binda this
överallt.
Bindar konsolfunktioner till variabler
var log = console.log.bind(console);
Användande:
log('one', '2', 3, [4], {5: 5});
Produktion:
one 2 3 [4] Object {5: 5}
Varför skulle du göra det?
Ett användningsfall kan vara när du har anpassad logger och du vill besluta om körtid vilken du vill använda.
var logger = require('appLogger');
var log = logToServer ? logger.log : console.log.bind(console);
Funktionsargument, "argument" objekt, vila och sprida parametrar
Funktioner kan ta ingångar i form av variabler som kan användas och tilldelas inom deras eget räckvidd. Följande funktion tar två numeriska värden och returnerar summan:
function addition (argument1, argument2){
return argument1 + argument2;
}
console.log(addition(2, 3)); // -> 5
arguments
objekt
arguments
innehåller alla funktionens parametrar som innehåller ett icke- standardvärde . Det kan också användas även om parametrarna inte uttryckligen deklareras:
(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]
Även när du skriver arguments
utgången liknar en Array, är det i själva verket ett objekt:
(function() { console.log(typeof arguments) })(); // -> object
Restparametrar: function (...parm) {}
I ES6 omvandlar syntaxen ...
när den används i deklarationen för en funktions parametrar variabeln till höger till ett enda objekt som innehåller alla återstående parametrar som tillhandahålls efter deklarerade. Detta gör att funktionen kan åberopas med ett obegränsat antal argument, som kommer att bli en del av denna variabel:
(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123
Spridningsparametrar: function_name(...varb);
I ES6 kan syntaxen ...
också användas när man åberopar en funktion genom att placera ett objekt / variabel till höger. Detta gör att objektets element kan överföras till den funktionen som ett enda objekt:
let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42
Namngivna funktioner
Funktioner kan antingen namnges eller inte namnges ( anonyma funktioner ):
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
Men deras namn är privata enligt deras eget räckvidd:
var sumTwoNumbers = function sum (a, b) {
return a + b;
}
sum(1, 3);
Unctained ReferenceError: summan är inte definierad
Namngivna funktioner skiljer sig från de anonyma funktionerna i flera scenarier:
- När du felsöker kommer namnet på funktionen att visas i felsökningen
- Namngivna funktioner lyfts medan anonyma funktioner inte är det
- Namngivna funktioner och anonyma funktioner uppträder annorlunda vid hantering av rekursion
- Beroende på ECMAScript version, namnges och anonyma funktioner kan behandla
name
egenskapen annorlunda
Namngivna funktioner lyftes
När du använder en anonym funktion kan funktionen endast anropas efter deklarationsraden, medan en namngiven funktion kan anropas före deklarationen. Överväga
foo();
var foo = function () { // using an anonymous function
console.log('bar');
}
Uncched TypeError: foo är inte en funktion
foo();
function foo () { // using a named function
console.log('bar');
}
bar
Namngivna funktioner i ett rekursivt scenario
En rekursiv funktion kan definieras som:
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);
Hej!
Hej!
Vad händer om någonstans i din kod den ursprungliga funktionsbindningen definieras om?
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Hej!
Uncched TypeError: say är inte en funktion
Detta kan lösas med en namngiven funktion
// 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);
Hej!
Hej!
Och som bonus kan den nämnda funktionen inte ställas in till undefined
, inte ens från insidan:
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);
Hej!
Hej!
Det name
egendom funktioner
Före ES6 hade namngivna funktioner deras name
egenskaper inställd på sina funktionsnamn, och anonyma funktioner hade deras name
egenskaper inställd på en tom sträng.
var foo = function () {}
console.log(foo.name); // outputs ''
function foo () {}
console.log(foo.name); // outputs 'foo'
Post ES6, namnges och icke namngivna fungerar både sätta sina name
egenskaper:
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'
Rekursiv funktion
En rekursiv funktion är helt enkelt en funktion som skulle kalla sig själv.
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Ovanstående funktion visar ett grundläggande exempel på hur man utför en rekursiv funktion för att returnera ett faktorial.
Ett annat exempel skulle vara att hämta summan av jämna siffror i en matris.
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);
}
Det är viktigt att sådana funktioner gör någon slags kontroll av värdet för att undvika oändliga slingor. I det första exemplet ovan, när n
är mindre än eller lika med 1, stannar rekursionen, vilket tillåter att resultatet av varje samtal återlämnas uppåt samtalsbunten.
Currying
Currying är omvandlingen av en funktion av n
arity eller argument till en sekvens av n
funktioner som endast tar ett argument.
Användningsfall: När värdena för vissa argument är tillgängliga före andra kan du använda currying för att sönderdela en funktion till en serie funktioner som slutför arbetet i steg eftersom varje värde anländer. Detta kan vara användbart:
- När värdet på ett argument nästan aldrig förändras (t.ex. en konverteringsfaktor), men du måste bibehålla flexibiliteten att ställa in det värdet (snarare än att hårdkoda det som en konstant).
- När resultatet av en curried-funktion är användbar innan de andra curried-funktionerna har körts.
- För att validera ankomsten av funktionerna i en specifik sekvens.
Till exempel kan volymen på ett rektangulärt prisma förklaras med en funktion av tre faktorer: längd ( l
), bredd ( w
) och höjd ( h
):
var prism = function(l, w, h) {
return l * w * h;
}
En curried version av den här funktionen ser ut som:
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;
Du kan ringa dessa sekvenser av funktioner med prism(2)(3)(5)
, som ska utvärderas till 30.
Utan några extra maskiner (som med bibliotek) är currying av begränsad syntaktisk flexibilitet i JavaScript (ES 5/6) på grund av bristen på platshållarvärden; så även om du kan använda var a = prism(2)(3)
att skapa en delvis tillämpad funktion , kan du inte använda prism()(3)(5)
.
Använda retursförklaringen
Återlämningsmeddelandet kan vara ett användbart sätt att skapa output för en funktion. Återlämningsmeddelandet är särskilt användbart om du inte vet i vilket sammanhang funktionen kommer att användas ännu.
//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);
}
För att använda den här funktionen måste du placera den i stället för en variabel någon annanstans i din kod:
Använda funktionsresultatet som ett argument för en annan funktion:
console.log(firstChar("Hello world"));
Konsolutgången kommer att vara:
> H
Returmeddelandet avslutar funktionen
Om vi ändrar funktionen i början kan vi visa att returrättet slutar funktionen.
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");
}
Att köra den här funktionen så kommer att se ut så här:
console.log(firstChar("JS"));
Konsolutgång:
> The first action of the first char function
> J
Det kommer inte att skriva ut meddelandet efter returrättet, eftersom funktionen nu är avslutad.
Returuttalande som sträcker sig över flera rader:
I JavaScript kan du normalt dela upp en kodrad i många rader för läsbarhetsändamål eller organisation. Detta är giltigt JavaScript:
var
name = "bob",
age = 18;
När JavaScript ser ett ofullständigt uttalande som var
ser det ut till nästa rad att slutföra sig själv. Men om du gör samma misstag med return
uttalande, kommer du inte få vad du förväntat.
return
"Hi, my name is "+ name + ". " +
"I'm "+ age + " years old.";
Denna kod kommer att returneras undefined
eftersom return
sig är ett fullständigt uttalande i Javascript, så den ser inte till nästa rad för att slutföra sig själv. Om du behöver dela upp en return
uttalande i flera rader, sätta ett värde bredvid retur innan du dela upp det, som så.
return "Hi, my name is " + name + ". " +
"I'm " + age + " years old.";
Vidarebefordra argument efter referens eller värde
I JavaScript skickas alla argument efter värde. När en funktion tilldelar ett nytt värde till en argumentvariabel kommer den förändringen inte att vara synlig för den som ringer:
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
Ändringar som gjorts i (kapslade) egenskaper för sådana argument kommer dock att vara synliga för den som ringer:
var obj = {a: 2};
function myfunc(arg){
arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5
Detta kan ses som ett samtal som referens : även om en funktion inte kan ändra den som ringer upp objektet genom att tilldela ett nytt värde till det, kan det mutera den som ringer upp objektet.
Eftersom primitiva värderade argument, som nummer eller strängar, är oföränderliga, finns det inget sätt för en funktion att mutera dem:
var s = 'say';
function myfunc(arg){
arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'
När en funktion vill mutera ett objekt som skickas som argument men inte vill mutera uppringarens objekt, bör argumentvariabeln tilldelas om:
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
Som ett alternativ till en mutation på plats av ett argument kan funktioner skapa ett nytt värde baserat på argumentet och returnera det. Den som ringer kan sedan tilldela den, även till den ursprungliga variabeln som skickades som argument:
var a = 2;
function myfunc(arg){
arg++;
return arg;
}
a = myfunc(a);
console.log(obj.a); // 3
Ring och ansök
Funktioner har två inbyggda metoder som gör det möjligt för programmeraren att tillhandahålla argument och this
variabel annorlunda: call
och apply
.
Detta är användbart eftersom funktioner som fungerar på ett objekt (objektet som de är en egenskap av) kan återanvändas för att fungera på ett annat, kompatibelt objekt. Dessutom kan argument ges i ett skott som matriser, liknande spridningsoperatören ( ...
) i 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 introducerade en annan metod som heter bind()
utöver call()
och apply()
att uttryckligen ställa in this
värde på funktionen till specifikt objekt.
Det beter sig helt annorlunda än de andra två. Det första argumentet som bind()
är this
värde för den nya funktionen. Alla andra argument representerar namngivna parametrar som bör ställas permanent i den nya funktionen.
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"
Standardparametrar
Före ECMAScript 2015 (ES6) kan en parameters standardvärde tilldelas på följande sätt:
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 tillhandahöll en ny syntax där tillståndet och omfördelningen som visas ovan inte längre är nödvändigt:
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!"
Detta visar också att om en parameter saknas när funktionen anropas, hålls dess värde som undefined
, eftersom den kan bekräftas genom att uttryckligen ange den i följande exempel (med hjälp av en pilfunktion ):
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"
Funktioner / variabler som standardvärden och återanvändningsparametrar
Standardparametrernas värden är inte begränsade till nummer, strängar eller enkla objekt. En funktion kan också ställas in som standardvärde callback = function(){}
:
function foo(callback = function(){ console.log('default'); }) {
callback();
}
foo(function (){
console.log('custom');
});
// custom
foo();
//default
Det finns vissa egenskaper hos de operationer som kan utföras genom standardvärden:
- En tidigare deklarerad parameter kan återanvändas som ett standardvärde för de kommande parametrarnas värden.
- Inlineoperationer är tillåtna när ett standardvärde tilldelas en parameter.
- Variabler som finns i samma räckvidd för den funktion som deklareras kan användas i dess standardvärden.
- Funktioner kan åberopas för att tillhandahålla deras returvärde till ett standardvärde.
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
Återanvända funktionens avkastningsvärde i en ny anrops standardvärde:
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
värde och längd när det saknas parametrar i anrop
arguments
arrayobjekt behåller bara de parametrar vars värden inte är standard, dvs. de som uttryckligen tillhandahålls när funktionen åberopas:
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
Funktioner med okänt antal argument (variadiska funktioner)
För att skapa en funktion som accepterar ett obestämt antal argument finns det två metoder beroende på din miljö.
När en funktion anropas har den ett Array-liknande argumentobjekt i sitt omfattning som innehåller alla argument som skickas till funktionen. Om du indexerar eller itererar över detta kommer du till exempel att få tillgång till argumenten
function logSomeThings() {
for (var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
}
}
logSomeThings('hello', 'world');
// logs "hello"
// logs "world"
Observera att du kan konvertera arguments
till en faktisk array vid behov; se: Konvertera Array-liknande objekt till Arrays
Från ES6 kan funktionen deklareras med sin sista parameter med hjälp av viloperatören ( ...
). Detta skapar en matris som håller argumenten från den punkten och framåt
function personLogsSomeThings(person, ...msg) {
msg.forEach(arg => {
console.log(person, 'says', arg);
});
}
personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"
Funktioner kan också kallas på liknande sätt, spridningssyntaxen
const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]
logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]
Denna syntax kan användas för att infoga godtyckligt antal argument till vilken position som helst, och kan användas med valfritt iterable ( apply
accepterar endast arrayliknande objekt).
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" ]
Få namnet på ett funktionsobjekt
ES6 :
myFunction.name
Förklaring på MDN . Från 2015 fungerar i nodejs och alla större webbläsare utom IE.
ES5 :
Om du har en referens till funktionen kan du göra:
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] : ''
}
Delvis tillämpning
I likhet med currying används partiell applikation för att minska antalet argument som skickas till en funktion. Till skillnad från currying behöver numret inte sjunka med en.
Exempel:
Denna funktion ...
function multiplyThenAdd(a, b, c) {
return a * b + c;
}
... kan användas för att skapa en annan funktion som alltid multiplicerar med 2 och sedan lägger till 10 till det passerade värdet;
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
"Applikations" -delen av partiell applikation betyder helt enkelt att fixa parametrar för en funktion.
Funktionskomposition
Att komponera flera funktioner i en är en vanlig funktionell programmering;
sammansättning gör en pipeline genom vilken våra data kommer att transiteras och modifieras helt enkelt genom att arbeta med funktionskompositionen (precis som att knyta bitar av ett spår tillsammans) ...
du börjar med några enskilda ansvarsfunktioner:
const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
const sign = x => x + ',\nmade with love';
och enkelt skapa ett transformationsspår:
const formatText = compose(capitalize, sign);
formatText('this is an example')
//This is an example,
//made with love
OBS! Komposition uppnås genom en verktygsfunktion som vanligtvis kallas compose
som i vårt exempel.
Implementering av compose
finns i många JavaScript-bibliotek ( lodash , rambda , etc.) men du kan också börja med en enkel implementering som:
const compose = (...funs) =>
x =>
funs.reduce((ac, f) => f(ac), x);