Zoeken…


Opmerkingen

Scope is de context waarin variabelen leven en toegankelijk zijn voor andere code in dezelfde scope. Omdat JavaScript grotendeels kan worden gebruikt als een functionele programmeertaal, is het belangrijk om de reikwijdte van variabelen en functies te kennen, omdat het helpt bij het voorkomen van bugs en onverwacht gedrag tijdens runtime.

Verschil tussen var en let

(Opmerking: alle voorbeelden met let zijn ook geldig voor const )

var is beschikbaar in alle versies van JavaScript, terwijl let en const deel uitmaken van ECMAScript 6 en alleen beschikbaar in sommige nieuwere browsers .

var valt onder de bevattende functie of de globale ruimte, afhankelijk van wanneer het wordt aangegeven:

var x = 4; // global scope

function DoThings() {
    var x = 7; // function scope
    console.log(x);
}

console.log(x); // >> 4
DoThings();     // >> 7
console.log(x); // >> 4

Dat betekent dat het "ontsnapt" if statements en alle vergelijkbare blokconstructies:

var x = 4;
if (true) {
    var x = 7;
}
console.log(x); // >> 7

for (var i = 0; i < 4; i++) {
    var j = 10;
}
console.log(i); // >> 4
console.log(j); // >> 10

Ter vergelijking: let we blokkeren:

let x = 4;

if (true) {
    let x = 7;
    console.log(x); // >> 7
}

console.log(x); // >> 4

for (let i = 0; i < 4; i++) {
    let j = 10;
}
console.log(i); // >> "ReferenceError: i is not defined"
console.log(j); // >> "ReferenceError: j is not defined"

Merk op dat i en j alleen in de for lus worden aangegeven en daarom daarbuiten niet worden aangegeven.

Er zijn verschillende andere cruciale verschillen:

Globale variabele verklaring

In het bovenste bereik (buiten alle functies en blokken) plaatsen var declaraties een element in het globale object. let niet:

var x = 4;
let y = 7;

console.log(this.x); // >> 4
console.log(this.y); // >> undefined

Re-verklaring

Twee keer een variabele declareren met var levert geen fout op (hoewel het equivalent is van één keer declareren):

var x = 4;
var x = 7;

Met let veroorzaakt dit een fout:

let x = 4;
let x = 7;

TypeError: Identifier x is al gedeclareerd

Hetzelfde geldt wanneer y wordt verklaard met var :

var y = 4;
let y = 7;

TypeError: Identifier y is al gedeclareerd

Variabelen die met let zijn gedeclareerd, kunnen echter opnieuw worden gebruikt (niet opnieuw) in een genest blok

let i = 5;    
{
   let i = 6;
   console.log(i); // >> 6
}
console.log(i); // >> 5

Binnen het blok is de buitenste i toegankelijk, maar als het binnenste blok een let verklaring voor i , is de buitenste i niet toegankelijk en zal deze een ReferenceError gooien indien gebruikt voordat de tweede is gedeclareerd.

let i = 5;
{
    i = 6;  // outer i is unavailable within the Temporal Dead Zone
    let i;
}

ReferenceError: i is niet gedefinieerd

Hijsen

Variabelen die zijn opgegeven met var en let worden gehesen . Het verschil is dat naar een variabele die met var is gedeclareerd, naar zijn eigen toewijzing kan worden verwezen, omdat deze automatisch wordt toegewezen (met undefined als zijn waarde), maar let niet - het vereist specifiek dat de variabele wordt gedeclareerd voordat deze wordt aangeroepen:

console.log(x); // >> undefined
console.log(y); // >> "ReferenceError: `y` is not defined"
//OR >> "ReferenceError: can't access lexical declaration `y` before initialization"
var x = 4;
let y = 7;

Het gebied tussen het begin van een blok en een let of const declaratie staat bekend als de Temporal Dead Zone en verwijzingen naar de variabele in dit gebied veroorzaken een ReferenceError . Dit gebeurt zelfs als de variabele wordt toegewezen voordat deze wordt gedeclareerd :

y=7; // >> "ReferenceError: `y` is not defined"
let y;

Als u in een niet-strikte modus een waarde toewijst aan een variabele zonder enige declaratie, wordt de variabele automatisch gedeclareerd in het globale bereik . In dit geval, in plaats van y automatisch in het globale bereik wordt verklaard, let reserves naam van de variabele ( y ) en geen enkele toegang of opdracht tot het voor de lijn waar het wordt gedeclareerd / geïnitialiseerd mogelijk te maken.

sluitingen

Wanneer een functie wordt gedeclareerd, worden variabelen in de context van de declaratie in het bereik vastgelegd. In de onderstaande code is de variabele x bijvoorbeeld gebonden aan een waarde in het buitenbereik en wordt de verwijzing naar x vastgelegd in de context van bar :

var x = 4; // declaration in outer scope

function bar() {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

Voorbeelduitvoer: 4

Dit concept van het "vastleggen" bereik is interessant omdat we variabelen van een buitenbereik kunnen gebruiken en wijzigen, zelfs nadat het buitenbereik is verlaten. Overweeg bijvoorbeeld het volgende:

function foo() {
    var x = 4; // declaration in outer scope

    function bar() {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;
    
    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

Voorbeelduitvoer: 4

In het bovenstaande voorbeeld, wanneer foo heet, de context wordt meegenomen in de functie bar . Dus zelfs nadat het is teruggekeerd, kan de bar de variabele x nog steeds openen en wijzigen. De functie foo , waarvan de context is vastgelegd in een andere functie, zou een sluiting zijn .

Privé gegevens

Dit laat ons enkele interessante dingen doen, zoals het definiëren van "private" variabelen die alleen zichtbaar zijn voor een specifieke functie of set functies. Een gekunsteld (maar populair) voorbeeld:

function makeCounter() {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(a.value());
console.log(b.value());

Voorbeeld output:

1
0

Wanneer makeCounter() wordt aangeroepen, wordt een momentopname van de context van die functie opgeslagen. Alle code in makeCounter() zal die momentopname gebruiken in hun uitvoering. Twee aanroepen van makeCounter() zullen dus twee verschillende snapshots maken, met hun eigen exemplaar van counter .

Onmiddellijk opgeroepen functie-expressies (IIFE)

Sluitingen worden ook gebruikt om mondiale vervuiling van de naamruimte te voorkomen, vaak door het gebruik van onmiddellijk opgeroepen functie-uitdrukkingen.

Direct opgeroepen functie-uitdrukkingen (of, misschien meer intuïtief, zelfuitvoerende anonieme functies ) zijn in wezen sluitingen die direct na verklaring worden genoemd. Het algemene idee met IIFE's is om het neveneffect van het creëren van een afzonderlijke context die alleen toegankelijk is voor de code binnen de IIFE te gebruiken.

Stel dat we naar jQuery willen verwijzen met $ . Overweeg de naïeve methode, zonder een IIFE te gebruiken:

var $ = jQuery;
// we've just polluted the global namespace by assigning window.$ to jQuery

In het volgende voorbeeld wordt een IIFE gebruikt om ervoor te zorgen dat de $ alleen aan jQuery is gebonden in de context die door de afsluiting is gecreëerd:

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn't exist, so no pollution

Zie het canonieke antwoord op Stackoverflow voor meer informatie over sluitingen.

Hijsen

Wat is hijsen?

Hijsen is een mechanisme dat alle variabele en functieverklaringen naar de top van hun bereik verplaatst. Variabele toewijzingen vinden echter nog steeds plaats waar ze oorspronkelijk waren.

Overweeg bijvoorbeeld de volgende code:

console.log(foo);  // → undefined
var foo = 42;
console.log(foo);  // → 42

De bovenstaande code is hetzelfde als:

var foo;             // → Hoisted variable declaration
console.log(foo);    // → undefined
foo = 42;            // → variable assignment remains in the same place
console.log(foo);    // → 42

Merk op dat vanwege het hijsen bovenstaande undefined niet hetzelfde is als het not defined resultaat van het uitvoeren:

console.log(foo);    // → foo is not defined 

Een soortgelijk principe is van toepassing op functies. Wanneer functies worden toegewezen aan een variabele (dat wil zeggen een functie-uitdrukking ), wordt de declaratie van de variabele opgeheven terwijl de toewijzing op dezelfde plaats blijft. De volgende twee codefragmenten zijn equivalent.

console.log(foo(2, 3));     // → foo is not a function

var foo = function(a, b) {
    return a * b;
}
var foo;
console.log(foo(2, 3));     // → foo is not a function
foo = function(a, b) {
    return a * b;
}

Bij het declareren van functie-instructies doet zich een ander scenario voor. In tegenstelling tot functieverklaringen, worden functieverklaringen boven aan het bereik gehesen. Overweeg de volgende code:

console.log(foo(2, 3));  // → 6
function foo(a, b) {
    return a * b;
}

De bovenstaande code is hetzelfde als het volgende codefragment vanwege het hijsen:

function foo(a, b) {
    return a * b;
}

console.log(foo(2, 3));  // → 6

Hier zijn enkele voorbeelden van wat wel en niet hijst:

// Valid code:
foo();

function foo() {}

// Invalid code:
bar();                     // → TypeError: bar is not a function
var bar = function () {};


// Valid code:
foo();
function foo() {
    bar();
}
function bar() {}


// Invalid code:
foo();
function foo() {
    bar();                // → TypeError: bar is not a function
}
var bar = function () {};


// (E) valid:
function foo() {
    bar();
}
var bar = function(){};
foo();

Beperkingen van hijsen

Het initialiseren van een variabele kan niet worden gehesen of in eenvoudige JavaScript-takels declaraties niet initialisatie.

Bijvoorbeeld: de onderstaande scripts geven verschillende uitvoer.

var x = 2; 
var y = 4; 
alert(x + y);

Dit geeft je een output van 6. Maar dit ...

var x = 2; 
alert(x + y);
var y = 4; 

Dit geeft u een uitvoer van NaN. Omdat we de waarde van y initialiseren, gebeurt het JavaScript-hijsen niet, dus de y-waarde is niet gedefinieerd. Het JavaScript zal overwegen dat y nog niet is aangegeven.

Het tweede voorbeeld is dus hetzelfde als hieronder.

var x = 2; 
var y;
alert(x + y);
y = 4; 

Dit geeft u een uitvoer van NaN.

voer hier de afbeeldingsbeschrijving in

Let-in-loops gebruiken in plaats van var (voorbeeld van klikhandlers)

Laten we zeggen dat we een knop moeten toevoegen voor elk stuk loadedData array (bijvoorbeeld, elke knop moet een schuifregelaar zijn die de gegevens toont; voor de eenvoud zullen we alleen een bericht waarschuwen). Je kunt zoiets proberen:

for(var i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

Maar in plaats van te waarschuwen, veroorzaakt elke knop de

TypeError: loadedData [i] is niet gedefinieerd

fout. Dit komt omdat het bereik van i het globale bereik (of een functiebereik) is en na de lus i == 3 . Wat we nodig hebben is niet om "de staat van i te onthouden". Dit kan gedaan worden met let :

for(let i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

Een voorbeeld van loadedData die met deze code moeten worden getest:

    var loadedData = [
        { label:"apple",      content:"green and round" },
        { label:"blackberry", content:"small black or blue" },
        { label:"pineapple",  content:"weird stuff.. difficult to explain the shape" }
    ];

Een viool om dit te illustreren

Aanroep van methode

Het aanroepen van een functie als methode van een object, de waarde this is dat object.

var obj = {
    name: "Foo",
    print: function () {
        console.log(this.name)
    }
}

We kunnen nu afdrukken gebruiken als een methode van obj. this zal obj zijn

obj.print();

Dit houdt dus in:

Foo

Anonieme aanroep

Het aanroepen van een functie als anonieme functie, this is het globale object ( self in de browser).

function func() {
    return this;
}

func() === window; // true
5

In de strikte modus van ECMAScript 5 is this niet undefined als de functie anoniem wordt aangeroepen.

(function () {
    "use strict";
    func();
}())

Dit wordt uitgevoerd

undefined

Aanroeper van de constructeur

Wanneer een functie wordt aangeroepen als constructor met het new trefwoord this neemt this de waarde van het object dat wordt geconstrueerd

function Obj(name) {
    this.name = name;
}

var obj = new Obj("Foo");

console.log(obj);

Dit zal loggen

{name: "Foo"}

Aanroep pijlfunctie

6

Bij het gebruik van pijlfuncties neemt this de waarde van de omsluitende uitvoeringscontext, this (dat wil zeggen, this in pijlfuncties heeft een lexicale scope in plaats van de gebruikelijke dynamische scope). In globale code (code die bij geen enkele functie hoort) zou dit het globale object zijn. En zo blijft het, zelfs als u de functie met de pijlnotatie oproept uit een van de andere methoden die hier worden beschreven.

var globalThis = this; //"window" in a browser, or "global" in Node.js

var foo = (() => this);           

console.log(foo() === globalThis);          //true

var obj = { name: "Foo" };
console.log(foo.call(obj) === globalThis);  //true

Kijk hoe this de context overneemt in plaats van te verwijzen naar het object waarop de methode is ingeschakeld.

var globalThis = this;

var obj = {
    withoutArrow: function() {
        return this;
    },
    withArrow: () => this
};

console.log(obj.withoutArrow() === obj);      //true
console.log(obj.withArrow() === globalThis);  //true

var fn = obj.withoutArrow; //no longer calling withoutArrow as a method
var fn2 = obj.withArrow;
console.log(fn() === globalThis);             //true
console.log(fn2() === globalThis);            //true

Toepassen en oproepsyntaxis en aanroep.

De apply en de call methoden in elke functie laat het een aangepaste waarde te bieden voor this .

function print() {
    console.log(this.toPrint);
}

print.apply({ toPrint: "Foo" }); // >> "Foo"
print.call({ toPrint: "Foo" }); // >> "Foo"

Je merkt misschien dat de syntaxis voor beide hierboven gebruikte aanroepen hetzelfde is. dwz De handtekening lijkt op elkaar.

Maar er is een klein verschil in hun gebruik, omdat we te maken hebben met functies en hun bereik wijzigen, moeten we nog steeds de oorspronkelijke argumenten aan de functie behouden. Beide zijn van apply en call ondersteuning door als volgt argumenten voor de doelfunctie:

function speak() {
    var sentences = Array.prototype.slice.call(arguments);
    console.log(this.name+": "+sentences);
}
var person = { name: "Sunny" };
speak.apply(person, ["I", "Code", "Startups"]); // >> "Sunny: I Code Startups"
speak.call(person, "I", "<3", "Javascript"); // >> "Sunny: I <3 Javascript"

Merk op dat je met de apply een Array of het arguments (matrixachtig) kunt doorgeven als de lijst met argumenten, terwijl voor call elk argument afzonderlijk moet worden doorgegeven.

Deze twee methoden geven u de vrijheid om zo chique te worden als u wilt, zoals het implementeren van een slechte versie van de native bind van ECMAScript om een functie te maken die altijd als methode voor een object uit een originele functie wordt aangeroepen.

function bind (func, obj) { 
    return function () {
        return func.apply(obj, Array.prototype.slice.call(arguments, 1));
    }
}

var obj = { name: "Foo" };

function print() {
    console.log(this.name);
}

printObj = bind(print, obj);

printObj();

Dit zal loggen

"Foo"


De bind functie heeft veel te doen

  1. obj zal als de waarde this worden gebruikt
  2. stuur de argumenten door naar de functie
  3. en retourneer vervolgens de waarde

Gebonden aanroeping

Met de bind van elke functie kunt u een nieuwe versie van die functie maken met de context strikt gebonden aan een specifiek object. Het is vooral handig om een functie te dwingen om als methode van een object te worden aangeroepen.

var obj = { foo: 'bar' };

function foo() {
    return this.foo;
}

fooObj = foo.bind(obj);

fooObj();

Dit zal loggen:

bar



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