Zoeken…


Invoering

Functies in JavaScript bieden georganiseerde, herbruikbare code om een reeks acties uit te voeren. Functies vereenvoudigen het coderingsproces, voorkomen overbodige logica en maken code eenvoudiger te volgen. Dit onderwerp beschrijft de declaratie en het gebruik van functies, argumenten, parameters, retourinstructies en bereik in JavaScript.

Syntaxis

  • functie voorbeeld (x) {return x}

  • var example = function (x) {return x}

  • (function () {...}) (); // Onmiddellijk ingeroepen functie-expressie (IIFE)

  • var instance = new Voorbeeld (x);

  • methoden

  • fn.apply (valueForThis [, arrayOfArgs])

  • fn.bind (valueForThis [, arg1 [, arg2, ...]])

  • fn.call (valueForThis [, arg1 [, arg2, ...]])

  • ES2015 + (ES6 +):

  • const voorbeeld = x => {return x}; // Pijlfunctie expliciet terug

  • const voorbeeld = x => x; // Pijlfunctie impliciet terug

  • const voorbeeld = (x, y, z) => {...} // Pijlfunctie meerdere argumenten

  • (() => {...}) (); // IIFE met een pijlfunctie

Opmerkingen

Raadpleeg de documentatie bij de pijlfuncties voor informatie over pijlfuncties.

Functies als een variabele

Een normale functieverklaring ziet er als volgt uit:

function foo(){
}

Een functie die op deze manier is gedefinieerd, is overal binnen de context toegankelijk via de naam. Maar soms kan het handig zijn om functieverwijzingen als objectverwijzingen te behandelen. U kunt bijvoorbeeld een object toewijzen aan een variabele op basis van een aantal voorwaarden en vervolgens later een eigenschap ophalen van het ene of het andere object:

var name = 'Cameron';
var spouse;

if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };

var spouseName = spouse.name;

In JavaScript kunt u hetzelfde doen met functies:

// Example 1
var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };

hash('Fred');

In het bovenstaande voorbeeld is hash een normale variabele. Het krijgt een verwijzing naar een functie, waarna de functie waarnaar het verwijst kan worden opgeroepen met haakjes, net als een normale functieverklaring.

Het bovenstaande voorbeeld verwijst naar anonieme functies ... functies die geen eigen naam hebben. U kunt ook variabelen gebruiken om naar benoemde functies te verwijzen. Het bovenstaande voorbeeld kan als volgt worden herschreven:

// 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){
    // ...
}

Of u kunt functieverwijzingen toewijzen aan objecteigenschappen:

// 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');

U kunt de verwijzing naar een functie van een variabele aan een andere toewijzen door de haakjes weg te laten. Dit kan leiden tot een gemakkelijk te maken fout: proberen de retourwaarde van een functie aan een andere variabele toe te wijzen, maar per ongeluk de verwijzing naar de functie toewijzen.

// 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;
}

Een verwijzing naar een functie is als elke andere waarde. Zoals u hebt gezien, kan een verwijzing worden toegewezen aan een variabele en de referentiewaarde van die variabele kan vervolgens worden toegewezen aan andere variabelen. U kunt verwijzingen naar functies zoals elke andere waarde doorgeven, inclusief een verwijzing naar een functie als de retourwaarde van een andere functie. Bijvoorbeeld:

// 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){
    // ...
}

U hoeft geen functieverwijzing aan een variabele toe te wijzen om deze te kunnen oproepen. In dit voorbeeld, voortbouwend op voorbeeld 5, wordt getHashingFunction aangeroepen en wordt de geretourneerde functie onmiddellijk opgeroepen en wordt de retourwaarde doorgegeven aan hashedValue.

// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );

Een opmerking over hijsen

Houd er rekening mee dat, in tegenstelling tot normale functieverklaringen, variabelen die verwijzen naar functies niet worden "gehesen". In voorbeeld 2 worden de functies md5Hash en sha1Hash onderaan het script gedefinieerd, maar zijn ze overal onmiddellijk beschikbaar. Het maakt niet uit waar u een functie definieert, de tolk "hijst" deze naar de top van zijn scope, waardoor deze onmiddellijk beschikbaar is. Dit is niet het geval voor variabele definities, dus code zoals de volgende zal breken:

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(){};

Anonieme functie

Een anonieme functie definiëren

Wanneer een functie is gedefinieerd, geeft u deze vaak een naam en roept u deze vervolgens op met die naam, als volgt:

foo();

function foo(){
    // ...
}

Wanneer u een functie op deze manier definieert, slaat de Javascript-runtime uw functie in het geheugen op en maakt vervolgens een verwijzing naar die functie met behulp van de naam die u hebt toegewezen. Die naam is dan toegankelijk binnen het huidige bereik. Dit kan een zeer handige manier zijn om een functie te maken, maar Javascript vereist niet dat u een naam aan een functie toewijst. Het volgende is ook volkomen legaal:

function() {
    // ...
}

Wanneer een functie zonder naam wordt gedefinieerd, staat deze bekend als een anonieme functie. De functie wordt in het geheugen opgeslagen, maar de runtime maakt er niet automatisch een verwijzing naar voor u aan. Op het eerste gezicht lijkt het misschien alsof zoiets geen zin heeft, maar er zijn verschillende scenario's waarin anonieme functies erg handig zijn.

Een anonieme functie toewijzen aan een variabele

Een veelgebruikt gebruik van anonieme functies is om ze toe te wijzen aan een variabele:

var foo = function(){ /*...*/ };

foo();

Dit gebruik van anonieme functies wordt meer gedetailleerd behandeld in Functies als een variabele

Een anonieme functie leveren als parameter voor een andere functie

Sommige functies accepteren mogelijk een verwijzing naar een functie als parameter. Deze worden soms "afhankelijkheidsinjecties" of "callbacks" genoemd, omdat het de functie die u aanroept in staat stelt om "terug te bellen" naar uw code, waardoor u de mogelijkheid hebt om het gedrag van de opgeroepen functie te wijzigen. Met de kaartfunctie van het Array-object kunt u bijvoorbeeld elk element van een array herhalen en vervolgens een nieuwe array maken door een transformatiefunctie op elk element toe te passen.

var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]

Het zou vervelend, slordig en onnodig zijn om een benoemde functie te maken, die je bereik zou verstoppen met een functie die alleen op deze plek nodig is en de natuurlijke stroom en het lezen van je code zou doorbreken (een collega zou deze code moeten verlaten om je te vinden functie om te begrijpen wat er aan de hand is).

Een anonieme functie retourneren vanuit een andere functie

Soms is het handig om een functie te retourneren als het resultaat van een andere functie. Bijvoorbeeld:

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 ){ /*...*/ };

}

Onmiddellijk een anonieme functie oproepen

In tegenstelling tot veel andere talen is scoping in Javascript op functieniveau en niet op blokniveau. (Zie Functie Scoping ). In sommige gevallen is het echter nodig om een nieuwe scope te maken. Het is bijvoorbeeld gebruikelijk om een nieuw bereik te maken bij het toevoegen van code via een <script> -tag, in plaats van toe te staan dat variabelenamen worden gedefinieerd in het globale bereik (waardoor het risico bestaat dat andere scripts botsen met uw variabelenamen). Een gebruikelijke methode om met deze situatie om te gaan, is een nieuwe anonieme functie te definiëren en deze vervolgens onmiddellijk in te schakelen, waarbij u veilig variabelen in het kader van de anonieme functie verbergt en zonder uw code toegankelijk te maken voor derden via een gelekte functienaam. Bijvoorbeeld:

<!-- 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>

Zelfreferente anonieme functies

Soms is het handig voor een anonieme functie om naar zichzelf te kunnen verwijzen. De functie moet bijvoorbeeld zichzelf recursief aanroepen of eigenschappen aan zichzelf toevoegen. Als de functie echter anoniem is, kan dit heel moeilijk zijn omdat het kennis vereist van de variabele waaraan de functie is toegewezen. Dit is de minder dan ideale oplossing:

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.

De bedoeling was dat de anonieme functie zichzelf recursief noemde, maar wanneer de waarde van foo verandert, krijg je een potentieel moeilijk te volgen bug.

In plaats daarvan kunnen we de anonieme functie een verwijzing naar zichzelf geven door deze een eigen naam te geven, zoals:

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?

Merk op dat de functienaam naar zichzelf reikt. De naam is niet naar buiten gelekt:

myself(false); // ReferenceError: myself is not defined

Deze techniek is vooral handig bij het omgaan met recursieve anonieme functies als callback-parameters:

5
// 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)

Onmiddellijk ingeroepen functie-uitdrukkingen

Soms wilt u niet dat uw functie toegankelijk / opgeslagen is als een variabele. U kunt een onmiddellijk aangeroepen functie-expressie maken (kortweg IIFE). Dit zijn in wezen zelfuitvoerende anonieme functies . Ze hebben toegang tot de omringende scope, maar de functie zelf en eventuele interne variabelen zijn van buitenaf niet toegankelijk. Een belangrijk ding om op te merken over IIFE is dat zelfs als u uw functie een naam geeft, IIFE niet wordt gehesen zoals standaardfuncties zijn en niet kunnen worden aangeroepen met de functienaam waarmee ze worden aangegeven.

(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");
}());

Dit is een andere manier om IIFE te schrijven. Merk op dat het sluitende haakje vóór de puntkomma werd verplaatst en direct achter de sluitende accolade werd geplaatst:

(function() {
   alert("This is IIFE too.");
})();

U kunt parameters eenvoudig doorgeven aan een IIFE:

(function(message) {
   alert(message);
}("Hello World!"));

Bovendien kunt u waarden retourneren naar het omliggende bereik:

var example = (function() {
   return 42;
}());
console.log(example); // => 42

Indien nodig is het mogelijk om een IIFE te noemen. Hoewel minder vaak gezien, heeft dit patroon verschillende voordelen, zoals het bieden van een referentie die kan worden gebruikt voor een recursie en debuggen eenvoudiger kan maken omdat de naam in de callstack is opgenomen.

(function namedIIFE() { 
    throw error; // We can now see the error thrown in 'namedIIFE()'
}());

Hoewel het omhullen van een functie tussen haakjes de meest gebruikelijke manier is om aan de Javascript-parser een uitdrukking te verwachten, op plaatsen waar een uitdrukking al wordt verwacht, kan de notatie beknopter worden gemaakt:

var a = function() { return 42 }();
console.log(a)  // => 42

Arrow-versie van de onmiddellijk aangeroepen functie:

6
(() => console.log("Hello!"))(); // => Hello!

Functie Scoping

Wanneer u een functie definieert, maakt deze een bereik .

Alles gedefinieerd binnen de functie is niet toegankelijk via code buiten de functie. Alleen code binnen dit bereik kan de binnen het bereik gedefinieerde entiteiten zien.

function foo() {
  var a = 'hello';
  console.log(a);  // => 'hello'
}

console.log(a);  // reference error

Geneste functies zijn mogelijk in JavaScript en dezelfde regels zijn van toepassing.

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

Wanneer JavaScript een verwijzing of variabele probeert op te lossen, begint het ernaar te zoeken in het huidige bereik. Als het die verklaring niet in de huidige scope kan vinden, klimt het één scope omhoog om ernaar te zoeken. Dit proces wordt herhaald totdat de aangifte is gevonden. Als de JavaScript-parser het globale bereik bereikt en de referentie nog steeds niet kan vinden, wordt een referentiefout gegenereerd.

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
  }
}

Dit klimgedrag kan ook betekenen dat een referentie kan 'schaduwen' over een referentie met dezelfde naam in het buitenbereik, omdat deze eerst wordt gezien.

var a = 'hello';

function foo() {
  var a = 'world';

  function bar() {
    console.log(a);  // => 'world'
  }
}
6

De manier waarop JavaScript scoping oplost, is ook van toepassing op het sleutelwoord const . Als u een variabele declareert met het sleutelwoord const betekent dit dat u de waarde niet opnieuw mag toewijzen, maar als u deze in een functie declareert, maakt u een nieuw bereik en daarmee een nieuwe variabele.

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
}

Functies zijn echter niet de enige blokken die een scope maken (als u let of const ). let en const declaraties hebben het bereik van de dichtstbijzijnde blokinstructie. Zie hier voor een meer gedetailleerde beschrijving.

Bindend 'dit' en argumenten

5.1

Wanneer u in JavaScript een verwijzing naar een methode (een eigenschap die een functie is) gebruikt, onthoudt deze meestal niet het object waaraan deze oorspronkelijk was gekoppeld. Als de methode naar dit object moet verwijzen, kan this niet en het aanroepen ervan zal waarschijnlijk een crash veroorzaken.

U kunt de methode .bind() voor een functie gebruiken om een wrapper te maken die de waarde this en een willekeurig aantal hoofdargumenten bevat.

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.

Wanneer niet in strikte modus, een functie gebruikt het globale object ( window in de browser) als this , tenzij de functie aangeroepen als een methode gebonden of opgeroepen met de methode .call syntax.

window.x = 12; 

function example() {
  return this.x;
}

console.log(example()); // 12

In de strikte modus is this standaard undefined

window.x = 12; 
    
function example() {
  "use strict";
  return this.x;
}

console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
7

Bind operator

De dubbele dubbele punt- operator kan worden gebruikt als een verkorte syntaxis voor het hierboven beschreven concept:

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

Met deze syntaxis kunt u normaal schrijven, zonder u zorgen te maken dat u this overal kunt binden.

Console-functies verbinden aan variabelen

var log = console.log.bind(console);

Gebruik:

log('one', '2', 3, [4], {5: 5});

Output:

one 2 3 [4] Object {5: 5}

Waarom zou je dat doen?

Eén use case kan zijn wanneer u een aangepaste logger hebt en u wilt beslissen over runtime welke u wilt gebruiken.

var logger = require('appLogger');

var log = logToServer ? logger.log : console.log.bind(console);

Functie Argumenten, "argumenten" -object, rust- en spreidingsparameters

Functies kunnen inputs bevatten in de vorm van variabelen die binnen hun eigen scope kunnen worden gebruikt en toegewezen. De volgende functie heeft twee numerieke waarden en retourneert hun som:

function addition (argument1, argument2){
    return argument1 + argument2; 
}

console.log(addition(2, 3)); // -> 5

arguments object

Het arguments bevat alle parameters van de functie die een niet- standaardwaarde bevatten. Het kan ook worden gebruikt als de parameters niet expliciet worden aangegeven:

(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]

Hoewel bij het afdrukken van arguments de uitvoer lijkt op een array, is het in feite een object:

(function() { console.log(typeof arguments) })(); // -> object

Rustparameters: function (...parm) {}

In ES6 transformeert de ... syntaxis bij gebruik in de declaratie van de parameters van een functie de variabele rechts ervan in een enkel object dat alle resterende parameters bevat die na de gedeclareerde parameters zijn opgegeven. Hierdoor kan de functie worden opgeroepen met een onbeperkt aantal argumenten, die onderdeel worden van deze variabele:

(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123  

Spread parameters: function_name(...varb);

In ES6 kan de ... syntaxis ook worden gebruikt bij het aanroepen van een functie door een object / variabele rechts ervan te plaatsen. Hiermee kunnen de elementen van dat object als een enkel object aan die functie worden doorgegeven:

let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42

Benoemde functies

Functies kunnen een naam of een naam hebben ( anonieme functies ):

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

Maar hun namen zijn privé voor hun eigen toepassingsgebied:

var sumTwoNumbers = function sum (a, b) {
    return a + b;
}

sum(1, 3);

Uncaught ReferenceError: sum is niet gedefinieerd

Benoemde functies verschillen van de anonieme functies in meerdere scenario's:

  • Wanneer u fouten opspoort, wordt de naam van de functie weergegeven in de fout- / stapeltracering
  • Benoemde functies worden gehesen , anonieme functies niet
  • Benoemde functies en anonieme functies gedragen zich anders bij het verwerken van recursie
  • Afhankelijk van de ECMAScript-versie, genaamd en anonieme functie kan de functie te behandelen name eigendom anders

Benoemde functies worden gehesen

Wanneer u een anonieme functie gebruikt, kan de functie alleen worden opgeroepen na de aangiftelijn, terwijl een benoemde functie vóór de aangifte kan worden aangeroepen. Overwegen

foo();
var foo = function () { // using an anonymous function
    console.log('bar');
}

Uncaught TypeError: foo is geen functie

foo();
function foo () { // using a named function
    console.log('bar');
}

bar


Benoemde functies in een recursief scenario

Een recursieve functie kan worden gedefinieerd als:

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);

Hallo!
Hallo!

Wat als ergens in uw code de oorspronkelijke functiebinding opnieuw wordt gedefinieerd?

var say = function (times) {
    if (times > 0) {
        console.log('Hello!');

        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

Hallo!
Uncaught TypeError: say is geen functie

Dit kan worden opgelost met een benoemde functie

// 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);

Hallo!
Hallo!

En als bonus kan de genoemde functie niet worden ingesteld op undefined , zelfs niet van binnenuit:

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);

Hallo!
Hallo!


De name eigendom van functies

Voordat ES6, genaamd functies hadden hun name eigenschappen in te stellen om hun functienamen en anonieme functie hadden hun name eigenschappen ingesteld op de lege string.

5
var foo = function () {}
console.log(foo.name); // outputs ''

function foo () {}
console.log(foo.name); // outputs 'foo'

Bericht ES6, benoemd en niet nader genoemde functies beiden zetten hun name eigenschappen:

6
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'

Recursieve functie

Een recursieve functie is gewoon een functie, die zichzelf zou noemen.

function factorial (n) {
    if (n <= 1) {
        return 1;
    }
    
    return n * factorial(n - 1);
}

De bovenstaande functie toont een eenvoudig voorbeeld van het uitvoeren van een recursieve functie om een faculteit te retourneren.


Een ander voorbeeld zou zijn om de som van even getallen in een array op te halen.

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);
}

Het is belangrijk dat dergelijke functies een soort schildwachtwaardecontrole uitvoeren om oneindige lussen te voorkomen. In het eerste voorbeeld hierboven, wanneer n kleiner is dan of gelijk is aan 1, stopt de recursie, waardoor het resultaat van elke oproep terug in de oproepstapel kan worden teruggestuurd.

Currying

Currying is de transformatie van een functie van n arity of argumenten in een reeks van n functies met slechts één argument.

Gebruiksgevallen: wanneer de waarden van sommige argumenten vóór andere beschikbaar zijn, kunt u curry gebruiken om een functie te ontleden in een reeks functies die het werk in fasen voltooien, naarmate elke waarde arriveert. Dit kan handig zijn:

  • Wanneer de waarde van een argument bijna nooit verandert (bijvoorbeeld een conversiefactor), maar u de flexibiliteit moet behouden om die waarde in te stellen (in plaats van het als een constante te coderen).
  • Wanneer het resultaat van een actieve functie nuttig is voordat de andere actieve functies zijn uitgevoerd.
  • De aankomst van de functies in een specifieke volgorde valideren.

Het volume van een rechthoekig prisma kan bijvoorbeeld worden verklaard door een functie van drie factoren: lengte ( l ), breedte ( w ) en hoogte ( h ):

var prism = function(l, w, h) {
    return l * w * h;
}

Een gecurryde versie van deze functie ziet er als volgt uit:

function prism(l) {
    return function(w) {
        return function(h) {
            return l * w * h;
        }
    }
}
6
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;

Je kunt deze reeks functies oproepen met prism(2)(3)(5) , die moet evalueren tot 30.

Zonder wat extra machines (zoals bij bibliotheken), is curry van beperkte syntactische flexibiliteit in JavaScript (ES 5/6) vanwege het gebrek aan tijdelijke waarden; dus, hoewel je var a = prism(2)(3) kunt gebruiken om een gedeeltelijk toegepaste functie te maken , kun je prism()(3)(5) .

Gebruik van de retourverklaring

De terugkeerinstructie kan een nuttige manier zijn om uitvoer voor een functie te maken. De return-instructie is vooral handig als u niet weet in welke context de functie nog zal worden gebruikt.

//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);
}

Om deze functie te gebruiken, moet u deze in plaats van een variabele ergens anders in uw code plaatsen:

Het functieresultaat gebruiken als argument voor een andere functie:

console.log(firstChar("Hello world"));

Console-uitvoer is:

> H

Het terugkeeroverzicht beëindigt de functie

Als we de functie in het begin wijzigen, kunnen we aantonen dat de terugkeerinstructie de functie beëindigt.

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");
}

Het uitvoeren van deze functie ziet er zo uit:

console.log(firstChar("JS"));

Console-uitgang:

> The first action of the first char function
> J

Het bericht wordt niet afgedrukt na de retourverklaring, omdat de functie nu is beëindigd.

Retourverklaring over meerdere regels:

In JavaScript kunt u normaal een regel code opsplitsen in meerdere regels voor leesbaarheid of organisatie. Dit is geldige JavaScript:

var
    name = "bob",
    age = 18;

Wanneer JavaScript een onvolledige instructie zoals var ziet, kijkt deze naar de volgende regel om zichzelf te voltooien. Als u echter dezelfde fout maakt met de return , krijgt u niet wat u had verwacht.

return
    "Hi, my name is "+ name + ". " +
    "I'm "+ age + " years old.";

Deze code retourneert undefined omdat return op zichzelf een volledige verklaring in Javascript is, dus het zal niet kijken naar de volgende regel om zichzelf te voltooien. Als u op te splitsen een return statement in meerdere regels, zet een waarde naast rendement voordat je het splitsen, zoals zo.

return "Hi, my name is " + name + ". " +
    "I'm " + age + " years old.";

Argumenten doorgeven op referentie of waarde

In JavaScript worden alle argumenten doorgegeven door waarde. Wanneer een functie een nieuwe waarde toekent aan een argumentvariabele, is die wijziging niet zichtbaar voor de beller:

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

Wijzigingen die zijn aangebracht in (geneste) eigenschappen van dergelijke argumenten, zijn echter wel zichtbaar voor de beller:

var obj = {a: 2};
function myfunc(arg){
    arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5

Dit kan worden gezien als een aanroep door verwijzing : hoewel een functie het object van de beller niet kan wijzigen door er een nieuwe waarde aan toe te kennen, kan het wel het object van de beller muteren .

Aangezien argumenten met primitieve waarden, zoals getallen of tekenreeksen, onveranderlijk zijn, kan een functie deze niet muteren:

var s = 'say';
function myfunc(arg){
    arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'

Wanneer een functie een doorgegeven object als argument wil muteren, maar het object van de beller niet daadwerkelijk wil muteren, moet de argumentvariabele opnieuw worden toegewezen:

6
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

Als alternatief voor een interne mutatie van een argument, kunnen functies een nieuwe waarde creëren op basis van het argument en deze retourneren. De beller kan het vervolgens toewijzen, zelfs aan de oorspronkelijke variabele die als argument is doorgegeven:

var a = 2;
function myfunc(arg){
    arg++;
    return arg;
}
a = myfunc(a);
console.log(obj.a); // 3

Bel en solliciteer

Functies hebben twee ingebouwde methoden waarmee de programmeur argumenten kan leveren en this variabele anders: call en apply .

Dit is handig, omdat functies die op het ene object werken (het object waarvan ze eigendom zijn) opnieuw kunnen worden gebruikt om op een ander, compatibel object te werken. Bovendien kunnen argumenten in één keer als arrays worden gegeven, vergelijkbaar met de spread-operator ( ... ) in 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 }
5

ECMAScript 5 introduceerde een andere methode genaamd bind() naast call() en apply() om this waarde van de functie expliciet in te stellen op een specifiek object.

Het gedraagt zich heel anders dan de andere twee. Het eerste argument om te bind() is de waarde this voor de nieuwe functie. Alle andere argumenten vertegenwoordigen benoemde parameters die permanent moeten worden ingesteld in de nieuwe functie.

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"

Standaard parameters

Vóór ECMAScript 2015 (ES6) kon de standaardwaarde van een parameter op de volgende manier worden toegewezen:

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 heeft een nieuwe syntaxis geboden waarbij de hierboven weergegeven voorwaarde en nieuwe toewijzing niet langer nodig is:

6
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!"

Dit laat ook zien dat als een parameter ontbreekt wanneer de functie wordt aangeroepen, de waarde ervan als undefined , omdat dit kan worden bevestigd door het expliciet in het volgende voorbeeld op te geven (met een pijlfunctie ):

6
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"

Functies / variabelen als standaardwaarden en parameters voor hergebruik

De waarden van de standaardparameters zijn niet beperkt tot getallen, tekenreeksen of eenvoudige objecten. Een functie kan ook worden ingesteld als de standaardwaarde callback = function(){} :

6
function foo(callback = function(){ console.log('default'); }) {
    callback();
}

foo(function (){
    console.log('custom');
});
// custom

foo();
//default

Er zijn bepaalde kenmerken van de bewerkingen die kunnen worden uitgevoerd via standaardwaarden:

  • Een eerder gedeclareerde parameter kan worden hergebruikt als standaardwaarde voor de waarden van de komende parameters.
  • Inline-bewerkingen zijn toegestaan bij het toewijzen van een standaardwaarde aan een parameter.
  • Variabelen die binnen hetzelfde bereik van de te declareren functie bestaan, kunnen in de standaardwaarden worden gebruikt.
  • Functies kunnen worden ingeroepen om hun retourwaarde in een standaardwaarde te zetten.
6
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 

De retourwaarde van de functie opnieuw gebruiken in de standaardwaarde van een nieuwe aanroep:

6
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 waarde en lengte bij ontbreken van parameters bij aanroep

Het arguments behoudt alleen de parameters waarvan de waarden niet standaard zijn, dat wil zeggen de parameters die expliciet worden opgegeven wanneer de functie wordt aangeroepen:

6
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

Functies met een onbekend aantal argumenten (variadische functies)

Om een functie te maken die een onbepaald aantal argumenten accepteert, zijn er twee methoden, afhankelijk van uw omgeving.

5

Wanneer een functie wordt aangeroepen, heeft deze een Array-achtig argumentenobject in het bereik, dat alle aan de functie doorgegeven argumenten bevat. Indexeren hierop of itereren zal bijvoorbeeld toegang geven tot de argumenten

function logSomeThings() {
    for (var i = 0; i < arguments.length; ++i) {
        console.log(arguments[i]);
    }
}

logSomeThings('hello', 'world');
// logs "hello"
// logs "world"

Merk op dat u indien nodig arguments kunt omzetten in een echte array; zie: Array-achtige objecten converteren naar arrays

6

Vanaf ES6 kan de functie met de laatste parameter worden opgegeven met behulp van de restoperator ( ... ). Dit creëert een array die de argumenten vanaf dat punt bevat

function personLogsSomeThings(person, ...msg) {
   msg.forEach(arg => {
       console.log(person, 'says', arg);
   });
}

personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"

Functies kunnen ook op dezelfde manier worden opgeroepen, de spread-syntaxis

const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]

logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]

Deze syntaxis kan worden gebruikt om een willekeurig aantal argumenten in te voegen op elke positie en kan worden gebruikt met elke iterabele ( apply accepteert alleen array-achtige objecten).

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" ]

Haal de naam van een functieobject op

6

ES6 :

myFunction.name

Uitleg over MDN . Vanaf 2015 werkt in nodejs en alle belangrijke browsers behalve IE.


5

ES5 :

Als u een verwijzing naar de functie hebt, kunt u het volgende doen:

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] : ''
}

Gedeeltelijke toepassing

Net als bij curry, wordt gedeeltelijke toepassing gebruikt om het aantal argumenten dat aan een functie wordt doorgegeven te verminderen. In tegenstelling tot curry hoeft het aantal niet met één te dalen.

Voorbeeld:

Deze functie ...

function multiplyThenAdd(a, b, c) {
    return a * b + c;
}

... kan worden gebruikt om een andere functie te maken die altijd met 2 vermenigvuldigt en dan 10 bij de doorgegeven waarde optelt;

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

Het "toepassingsgedeelte" van gedeeltelijke toepassing betekent eenvoudigweg het vaststellen van parameters van een functie.

Functie samenstelling

Het samenstellen van meerdere functies in één is een gebruikelijke praktijk van programmeren;

compositie maakt een pijplijn waardoor onze gegevens worden doorgevoerd en aangepast worden door simpelweg aan de functie-compositie te werken (net als stukjes van een track aan elkaar klikken) ...

je begint met enkele functies voor één verantwoordelijkheid:

6
 const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
 const sign = x => x + ',\nmade with love';

en maak eenvoudig een transformatiespoor:

6
 const formatText = compose(capitalize, sign);

 formatText('this is an example')
 //This is an example,
 //made with love

NB Samenstelling wordt bereikt door een gebruiksfunctie die meestal compose zoals in ons voorbeeld.

Implementatie van compose is aanwezig in veel JavaScript-hulpprogramma-bibliotheken ( lodash , rambda , etc.) maar u kunt ook beginnen met een eenvoudige implementatie, zoals:

6
 const compose = (...funs) =>
   x =>
   funs.reduce((ac, f) => f(ac), x);


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow