[Javascript] Zelf uitvoerende anonieme functie

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Ik ben de afgelopen tijd wat dieper me aan het verdiepen geweest in JS en ik heb meerdere malen het volgende als "good practice" voorbij zien komen

JavaScript:
1
2
3
(function(){
    var foo = 'Hello world';
})();


De werking hiervan is mij volstrekt helder. Mijn grote vraag is alleen waarom je dit in vredesnaam zo zou willen... Ik begrijp helemaal dat je hiermee voorkomt dat variabelen in de global scope terecht komen, maar wat is hier beter aan dan

JavaScript:
1
2
3
4
5
function doFoo() {
    var foo = "hello world";
}

doFoo();


Ik snap dat de eerste variant een paar regels korter is, maar de tweede is imho een stuk leesbaarder...

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 22-09 16:31
Omdat bij de eerste ook de hele functie uit de scope verdwijnt.

Het nut is nogal beperkt, gebruik het eigenlijk zelden, maar het ziet er leuk uit.

[ Voor 40% gewijzigd door Bosmonster op 15-02-2012 13:13 ]


Acties:
  • 0 Henk 'm!

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Bosmonster schreef op woensdag 15 februari 2012 @ 13:12:
Omdat bij de eerste ook de hele functie uit de scope verdwijnt.

Het nut is nogal beperkt, gebruik het eigenlijk zelden, maar het ziet er leuk uit.
Je bedoeld dat doFoo in mijn eigen variant wel in scope is? Tja, daar zit wat in maar dat weegt imho niet op tegen het gebrek aan leesbaarheid van de 1e variant.

Acties:
  • 0 Henk 'm!

  • Tim
  • Registratie: Mei 2000
  • Laatst online: 04-08 16:29

Tim

Het is vooral om te voorkomen dat je per ongeluk functies of variabele in andere bestanden overschrijft.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 03:42
Ik gebruik dit zelf wel eens om een closure met lexical scoping te emuleren. (De nieuwste JavaScript standaard krijgt support voor lexical scoping trouwens, maar als je daar op moet wachten...)

Da's handig bij het definiëren van event handlers bijvoorbeeld, vooral als je een closure als event handler wil gebruiken (en dus variabelen uit de buitenste functie scope wil gebruiken), bijvoorbeeld:

JavaScript:
1
2
3
4
5
6
7
8
9
function foo()
{
    var prefix = "nr. "
    for (var i = 1; i <= 10; ++i) {
        (function(i){
           setTimeout(function(){ alert(prefix + i) }, i*1000)
        })(i)
    }
}


Verder zou ik de vraag kunnen omdraaien: waarom zou je een lokale functie globale scope geven als dat niet nodig is? Scoping is een belangrijke tool om de complexiteit van je programma te beheersen en implementatie en interface te scheiden. Om die reden gebruik je bijvoorbeeld lokale variabelen (in plaats van uitsluitend globale); waarom zouden (niet-functie-)variabelen wel lokaal mogen zijn en functie-variabelen/expressies niet?

Als je het nut erkent van lokale variabelen t.o.v. globale variabelen, dunkt me dat je ook het nut van lokale functies zou moeten kunnen inzien.

[ Voor 56% gewijzigd door Soultaker op 15-02-2012 21:32 ]


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 11:19

crisp

Devver

Pixelated

http://benalman.com/news/...oked-function-expression/ aka een 'iffy' :P - ik ben het hier eens met Ben Alman dat 'Zelf uitvoerende anonieme functie' (self-invoking anonymous function) de lading van dit pattern niet geheel dekt ;)

Het nut is niet beperkt tot enkel het afschermen van variabelen uit de global scope; dit 'pattern' biedt veel meer voordelen. Een paar voorbeelden:

Google analytics' async snippet:
JavaScript:
1
2
3
4
(function(d,t){
    var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
    g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s);
})(document,'script');

Buiten het feit dat dit niet je global scope vervuilt met de tijdelijke variabelen die gebruikt worden zie je dat je met dit pattern ook argumenten mee kan geven die er uiteindelijk voor zorgen dat de totale boilerplate van de snippet nog kleiner is :)

Ander voorbeeld: een interne 'state' kunnen bewaren:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var getById = function()
{
    var idCache = {};

    return function(id)
    {
        if (!(id in idCache))
        {
            idCache[id] = document.getElementById(id);
        }

        return idCache[id];
    }
}();


Dit lijkt niet meer geheel op het eerste voorbeeld (de haakjes om de functie expressie zijn hier ook niet nodig), en je zou mogelijk denken dat deze functie ook niet 'anonymous' meer is, maar niets is minder waar. Uiteraard had ik het ook zo kunnen schrijven:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function()
{
    var idCache = {};

    window.getById = function(id)
    {
        if (!(id in idCache))
        {
            idCache[id] = document.getElementById(id);
        }

        return idCache[id];
    }   
})();
:P

En niet alleen kan je dit pattern gebruiken om interne state te bewaren zonder een instance van een object te hoeven maken, je kan er ook 'bootstrap'-logica in stoppen en afhankelijk van bepaalde condities iets anders 'exposen':

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// just an example, don't use this in real world code!
var addEvent = function()
{
    if (document.addEventListener)
    {
        return function(element, type, handler)
        {
            element.addEventListener(type, handler, false);
        }
    }

    if (document.attachEvent)
    {
        return function(element, type, handler)
        {
            element.attachEvent('on' + evnt, func);
        }
    }

    return function()
    {
        throw('What kind of browser are you using?');
    }
}();


Voordeel hier is dat de evaluatie van welke 'branch' van de logica moet worden uitgevoerd bij het aanroepen van deze functie in een bepaalde browser maar 1 maal gedaan hoeft te worden.

En last but not least kan je dit pattern gebruiken om je eigen library eenvoudig te voorzien van een 'namespace', private vars en methods (tevens meteen een prachtig singleton-pattern):

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var MyLibrary = function()
{
    var privateVar = 'private';

    function privateFunction()
    {
        alert('I am a private function!');
    }

    var publicFunction = function()
    {
        alert('I have access to ' + privateVar);
        privateFunction();
    }

    return {
        publicFunction: publicFunction
    };
}();

MyLibrary.publicFunction();


edit: en Soultaker's voorbeeld van lexical scoping met behulp van dit pattern is natuurlijk ook een prachtige usecase :)

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 22-09 16:31
Thanks crisp. (Lexical) scoping is nu ongeveer dat enige waar ik het af en toe voor gebruik. Zitten interessante cases tussen.

Mooie post!

[ Voor 5% gewijzigd door Bosmonster op 15-02-2012 23:30 ]


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 11:19

crisp

Devver

Pixelated

Lexical scope kan je ook zo emuleren:
JavaScript:
1
2
3
4
5
6
7
8
9
function foo()
{
    var prefix = "nr. "
    for (var i = 1; i <= 10; ++i) {
        with({i:i}) {
           setTimeout(function(){ alert(prefix + i) }, i*1000)
        }
    }
}

:)

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
crisp schreef op zondag 19 februari 2012 @ 21:01:
Lexical scope kan je ook zo emuleren:
[...]
:)
Alleen is het gebruik van 'with' dan in het algemeen weer geaccepteerd als een anti-pattern. ;)

Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 11:19

crisp

Devver

Pixelated

R4gnax schreef op woensdag 22 februari 2012 @ 08:58:
[...]

Alleen is het gebruik van 'with' dan in het algemeen weer geaccepteerd als een anti-pattern. ;)
True, maar dat wil niet zeggen dat er geen goede usecases voor zijn ;)

Het maakt alleen variable minification weer lastig...

[ Voor 9% gewijzigd door crisp op 22-02-2012 09:39 ]

Intentionally left blank

Pagina: 1