Suche…


Bemerkungen

Bereich ist der Kontext, in dem Variablen leben und auf den mit anderem Code im selben Bereich zugegriffen werden kann. Da JavaScript weitgehend als funktionale Programmiersprache verwendet werden kann, ist es wichtig, den Umfang der Variablen und Funktionen zu kennen, da Fehler und unerwartetes Verhalten zur Laufzeit vermieden werden.

Unterschied zwischen var und let

(Hinweis: Alle Beispiele, die let gelten auch für const )

var ist in allen JavaScript-Versionen verfügbar, während let und const Teil von ECMAScript 6 und nur in einigen neueren Browsern verfügbar sind .

var ist abhängig von der Deklaration auf die enthaltende Funktion oder den globalen Raum beschränkt:

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

Das heißt, es "entgeht", if Anweisungen und alle ähnlichen Blockkonstrukte:

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

Zum Vergleich: let is block scoped:

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"

Beachten Sie, dass i und j nur in der for Schleife deklariert sind und daher außerhalb von ihr nicht deklariert werden.

Es gibt einige andere entscheidende Unterschiede:

Globale Variablendeklaration

Im oberen Bereich (außerhalb von Funktionen und Blöcken) var Deklarationen ein Element in das globale Objekt ein. let nicht:

var x = 4;
let y = 7;

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

Re-Deklaration

Wenn Sie eine Variable zweimal mit var deklarieren, wird kein Fehler ausgegeben (obwohl sie gleichbedeutend ist, wenn Sie sie einmal deklarieren):

var x = 4;
var x = 7;

Mit let erzeugt dies einen Fehler:

let x = 4;
let x = 7;

TypeError: Bezeichner x wurde bereits deklariert

Dasselbe gilt, wenn y mit var deklariert wird:

var y = 4;
let y = 7;

TypeError: Identifier y wurde bereits deklariert

Mit let deklarierte Variablen können jedoch in einem geschachtelten Block wiederverwendet (nicht erneut deklariert) werden

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

Innerhalb des Blocks kann auf das äußere i zugegriffen werden. Wenn der innere Block jedoch eine let Deklaration für i , kann auf das äußere i nicht zugegriffen werden, und es wird ein ReferenceError wenn der zweite Block vor der Deklaration verwendet wird.

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

ReferenceError: i ist nicht definiert

Heben

Variablen, die sowohl mit var als auch let deklariert wurden let werden angehoben . Der Unterschied besteht darin, dass eine mit var deklarierte Variable vor ihrer eigenen Zuweisung referenziert werden kann, da sie automatisch zugewiesen wird (mit undefined als Wert). let jedoch nicht, da die Variable vor dem Aufruf deklariert werden muss.

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;

Der Bereich zwischen dem Beginn eines Blocks und einer let oder const Deklaration wird als temporale Totzone bezeichnet. Verweise auf die Variable in diesem Bereich führen zu einem ReferenceError . Dies geschieht auch, wenn die Variable vor der Deklaration zugewiesen wird :

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

Wenn Sie einer Variablen ohne Deklaration einen Wert zuweisen, wird die Variable automatisch im globalen Bereich deklariert . In diesem Fall wird anstelle von y automatisch im globalen Bereich erklärt werden, let Reserven den Namen der Variablen ( y ) und erlauben keinen Zugriff oder Zuweisung , um es vor der Zeile , wo sie deklariert / initialisiert wird.

Verschlüsse

Wenn eine Funktion deklariert wird, werden Variablen im Kontext ihrer Deklaration in ihrem Gültigkeitsbereich erfasst. Im folgenden Code wird beispielsweise die Variable x an einen Wert im äußeren Bereich gebunden, und dann wird der Verweis auf x im Kontext von bar erfasst:

var x = 4; // declaration in outer scope

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

bar(); // prints 4 to console

Musterausgabe: 4

Dieses Konzept des "Erfassungsbereichs" ist interessant, da wir auch nach dem Beenden des äußeren Bereichs Variablen aus einem äußeren Bereich verwenden und modifizieren können. Betrachten Sie zum Beispiel Folgendes:

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

Musterausgabe: 4

In dem obigen Beispiel, wenn foo genannt wird , wird ihr Kontext in der Funktion erfaßt bar . bar kann also auch nach der Rückkehr auf die Variable x zugreifen und diese ändern. Die Funktion foo , deren Kontext in einer anderen Funktion erfasst wird, wird als Schließung bezeichnet .

Private Daten

Dadurch können wir einige interessante Dinge tun, beispielsweise die Definition "privater" Variablen, die nur für eine bestimmte Funktion oder eine Gruppe von Funktionen sichtbar sind. Ein erfundenes (aber beliebtes) Beispiel:

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

Beispielausgabe:

1
0

Wenn makeCounter() aufgerufen wird, wird eine Momentaufnahme des Kontextes dieser Funktion gespeichert. Der gesamte Code in makeCounter() verwendet diesen Snapshot bei der Ausführung. Bei zwei Aufrufen von makeCounter() werden somit zwei verschiedene Momentaufnahmen mit einer eigenen Kopie des counter .

Sofort aufgerufene Funktionsausdrücke (IIFE)

Closures werden auch verwendet, um die Verschmutzung des globalen Namensraums zu verhindern, häufig durch die Verwendung von sofort aufgerufenen Funktionsausdrücken.

Sofort aufgerufene Funktionsausdrücke (oder, intuitiver, anonyme Funktionen , die sich selbst ausführen lassen ) sind im Wesentlichen Schließungen, die direkt nach der Deklaration aufgerufen werden. Die allgemeine Idee bei IIFE ist, den Nebeneffekt der Erstellung eines separaten Kontexts aufzurufen, auf den nur der Code innerhalb der IIFE zugreifen kann.

Nehmen wir an, wir möchten jQuery mit $ referenzieren. Betrachten Sie die naive Methode, ohne einen IIFE zu verwenden:

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

Im folgenden Beispiel wird ein IIFE verwendet, um sicherzustellen, dass $ nur in dem durch die Schließung erstellten Kontext an jQuery gebunden ist:

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

Weitere Informationen zu Schließungen finden Sie in der kanonischen Antwort zu Stackoverflow .

Heben

Was ist das Heben?

Beim Hochziehen handelt es sich um einen Mechanismus, mit dem alle Variablen- und Funktionsdeklarationen ganz nach oben verschoben werden. Variable Zuweisungen finden jedoch weiterhin dort statt, wo sie ursprünglich waren.

Betrachten Sie zum Beispiel den folgenden Code:

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

Der obige Code ist derselbe wie:

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

Beachten Sie, dass aufgrund des Anhebens das obige undefined nicht dasselbe ist wie das not defined Ergebnis des Laufens:

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

Ein ähnliches Prinzip gilt für Funktionen. Wenn Funktionen einer Variablen zugewiesen werden (dh einem Funktionsausdruck ), wird die Variablendeklaration angehoben, während die Zuweisung an derselben Stelle bleibt. Die folgenden zwei Codeausschnitte sind gleichwertig.

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

Bei der Deklaration von Funktionsanweisungen tritt ein anderes Szenario auf. Im Gegensatz zu Funktionsanweisungen werden Funktionsdeklarationen ganz oben in ihren Geltungsbereich gehoben. Betrachten Sie den folgenden Code:

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

Der obige Code ist derselbe wie der nächste Codeausschnitt aufgrund des Hochziehens:

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

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

Hier sind einige Beispiele von was ist und was nicht hebt:

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

Einschränkungen beim Anheben

Das Initialisieren einer Variablen kann nicht angehoben werden oder In einfachen JavaScript-Deklinationen kann Hoists nicht initialisiert werden.

Zum Beispiel: Die folgenden Skripte liefern unterschiedliche Ausgaben.

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

Dies ergibt eine Ausgabe von 6. Aber das ...

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

Dadurch erhalten Sie eine Ausgabe von NaN. Da wir den Wert von y initialisieren, geschieht das JavaScript-Hoisting nicht, daher ist der y-Wert undefiniert. Das JavaScript berücksichtigt, dass y noch nicht deklariert ist.

Das zweite Beispiel ist also wie folgt.

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

Dadurch erhalten Sie eine Ausgabe von NaN.

Geben Sie hier die Bildbeschreibung ein

Verwenden von let in loops anstelle von var

Nehmen wir an, wir müssen für jedes loadedData Array eine Schaltfläche hinzufügen (z. B. sollte jede Schaltfläche ein Schieberegler sein, der die Daten loadedData ; zur Vereinfachung wird eine Meldung loadedData ). Man kann so etwas versuchen:

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

Anstatt zu warnen, bewirkt jede Taste das

TypeError: loadedData [i] ist nicht definiert

Error. Dies liegt daran, dass der Gültigkeitsbereich von i der globale Gültigkeitsbereich (oder ein Funktionsumfang) und nach der Schleife i == 3 . Was wir brauchen, ist nicht "sich an den Zustand von i zu erinnern". Dies kann mit 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); });

Ein Beispiel für loadedData , das mit diesem Code getestet werden soll:

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

Eine Geige, um das zu veranschaulichen

Methodenaufruf

Wenn Sie eine Funktion als Methode eines Objekts aufrufen, wird der Wert this Objekts dieses Objekt sein.

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

Wir können jetzt print als Methode von obj aufrufen. this wird obj sein

obj.print();

Dies wird also protokollieren:

Foo

Anonymer Aufruf

Unter Berufung auf eine Funktion als eine anonyme Funktion, this wird das globale Objekt ( self im Browser).

function func() {
    return this;
}

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

Im strikten Modus von ECMAScript 5 ist this undefined wenn die Funktion anonym aufgerufen wird.

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

Dies wird ausgegeben

undefined

Aufruf des Konstruktors

Wenn eine Funktion als Konstruktor aufgerufen wird , mit dem new Schlüsselwort this nimmt den Wert des Objekts ausgebildet ist

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

var obj = new Obj("Foo");

console.log(obj);

Dies wird protokollieren

{name: "foo"}

Aufruf der Pfeilfunktion

6

Wenn Sie Pfeilfunktionen verwenden, übernimmt this den Wert aus dem umgebenden Ausführungskontext. this dass this Funktion in Pfeilfunktionen einen lexikalischen Bereich und nicht den üblichen dynamischen Bereich hat. Im globalen Code (Code, der zu keiner Funktion gehört) wäre es das globale Objekt. Dies bleibt auch dann der Fall, wenn Sie die mit der Pfeilnotation deklarierte Funktion aus einer der anderen hier beschriebenen Methoden aufrufen.

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

Sehen Sie, wie this den Kontext erbt, anstatt sich auf das Objekt zu beziehen, für das die Methode aufgerufen wurde.

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

Übernehmen und Aufruf von Syntax und Aufruf

Die apply und call Methoden in jeder Funktion ermöglicht es , einen eigenen Wert zu liefern this .

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

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

Möglicherweise stellen Sie fest, dass die Syntax für die beiden oben verwendeten Aufrufe dieselbe ist. dh Die Signatur sieht ähnlich aus.

Es gibt jedoch einen kleinen Unterschied in ihrer Verwendung, da wir uns mit Funktionen und deren Änderung befassen, müssen wir die ursprünglichen Argumente beibehalten, die an die Funktion übergeben werden. apply und call Unterstützung für die Weitergabe von Argumenten an die Zielfunktion auf:

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"

Beachten Sie, dass apply es Ihnen ermöglicht, ein Array oder das arguments (array-like) als Liste von Argumenten zu übergeben, wohingegen Sie beim call jedes Argument separat übergeben müssen.

Diese beiden Methoden geben Ihnen die Freiheit, so schick zu werden, wie Sie möchten, z. B. die Implementierung einer schlechten Version des nativen ECMAScript- bind , um eine Funktion zu erstellen, die immer als Methode eines Objekts aus einer Originalfunktion aufgerufen wird.

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

Dies wird protokollieren

"Foo"


Die bind hat viel zu tun

  1. obj wird als Wert verwendet werden , this
  2. leiten Sie die Argumente an die Funktion weiter
  3. und dann den Wert zurückgeben

Gebundene Anrufung

Mit der bind jeder Funktion können Sie eine neue Version dieser Funktion erstellen, deren Kontext streng an ein bestimmtes Objekt gebunden ist. Es ist besonders nützlich, eine Funktion als Methode eines Objekts aufzurufen.

var obj = { foo: 'bar' };

function foo() {
    return this.foo;
}

fooObj = foo.bind(obj);

fooObj();

Dies wird protokollieren:

Bar



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow