...) in Funktionsaufrufen.call(), .apply(), .bind().call().apply().bind()In diesem Kapitel betrachten wir JavaScript-Werte, die aufgerufen werden können: Funktionen, Methoden und Klassen.
JavaScript hat zwei Kategorien von Funktionen
Eine gewöhnliche Funktion kann mehrere Rollen spielen
Eine spezialisierte Funktion kann nur eine dieser Rollen spielen – zum Beispiel
Spezialisierte Funktionen wurden der Sprache in ECMAScript 6 hinzugefügt.
Lesen Sie weiter, um herauszufinden, was all das bedeutet.
Der folgende Code zeigt zwei Wege, (ungefähr) dasselbe zu tun: eine gewöhnliche Funktion erstellen.
// Function declaration (a statement)
function ordinary1(a, b, c) {
// ···
}
// const plus anonymous (nameless) function expression
const ordinary2 = function (a, b, c) {
// ···
};Innerhalb eines Gültigkeitsbereichs werden Funktionsdeklarationen frühzeitig aktiviert (siehe §11.8 „Deklarationen: Gültigkeitsbereich und Aktivierung“) und können vor ihrer Deklaration aufgerufen werden. Das ist gelegentlich nützlich.
Variablendeklarationen, wie die für ordinary2, werden nicht frühzeitig aktiviert.
Bisher haben wir nur anonyme Funktionsausdrücke gesehen – die keine Namen haben
const anonFuncExpr = function (a, b, c) {
// ···
};Aber es gibt auch benannte Funktionsausdrücke
const namedFuncExpr = function myName(a, b, c) {
// `myName` is only accessible in here
};myName ist nur innerhalb des Funktionskörpers zugänglich. Die Funktion kann ihn verwenden, um auf sich selbst zu verweisen (für Selbst-Rekursion usw.) – unabhängig davon, welcher Variablen sie zugewiesen ist
const func = function funcExpr() { return funcExpr };
assert.equal(func(), func);
// The name `funcExpr` only exists inside the function body:
assert.throws(() => funcExpr(), ReferenceError);Selbst wenn sie keinen Variablen zugewiesen sind, haben benannte Funktionsausdrücke Namen (Zeile A)
function getNameOfCallback(callback) {
return callback.name;
}
assert.equal(
getNameOfCallback(function () {}), ''); // anonymous
assert.equal(
getNameOfCallback(function named() {}), 'named'); // (A)Beachten Sie, dass Funktionen, die über Funktionsdeklarationen oder Variablendeklarationen erstellt werden, immer Namen haben
function funcDecl() {}
assert.equal(
getNameOfCallback(funcDecl), 'funcDecl');
const funcExpr = function () {};
assert.equal(
getNameOfCallback(funcExpr), 'funcExpr');Ein Vorteil von benannten Funktionen ist, dass diese Namen in Fehler-Stack-Traces erscheinen.
Eine Funktionsdefinition ist eine Syntax, die Funktionen erstellt
Funktionsdeklarationen erzeugen immer gewöhnliche Funktionen. Funktionsausdrücke erzeugen entweder gewöhnliche Funktionen oder spezialisierte Funktionen
Während Funktionsdeklarationen in JavaScript immer noch beliebt sind, sind Funktionsausdrücke im modernen Code fast immer Pfeilfunktionen.
Untersuchen wir die Teile einer Funktionsdeklaration anhand des folgenden Beispiels. Die meisten Begriffe gelten auch für Funktionsausdrücke.
function add(x, y) {
return x + y;
}add ist der Name der Funktionsdeklaration.add(x, y) ist der Kopf der Funktionsdeklaration.x und y sind die Parameter.{ und }) und alles dazwischen sind der Körper der Funktionsdeklaration.return-Anweisung gibt explizit einen Wert aus der Funktion zurück.JavaScript hat schon immer nachgestellte Kommas in Array-Literalen erlaubt und ignoriert. Seit ES5 sind sie auch in Objekt-Literalen erlaubt. Seit ES2017 können wir nachgestellte Kommas zu Parameterlisten (Deklarationen und Aufrufen) hinzufügen
// Declaration
function retrieveData(
contentText,
keyword,
{unique, ignoreCase, pageSize}, // trailing comma
) {
// ···
}
// Invocation
retrieveData(
'',
null,
{ignoreCase: true, pageSize: 10}, // trailing comma
);Betrachten Sie die folgende Funktionsdeklaration aus dem vorherigen Abschnitt
function add(x, y) {
return x + y;
}Diese Funktionsdeklaration erstellt eine gewöhnliche Funktion mit dem Namen add. Als gewöhnliche Funktion kann add() drei Rollen spielen
Echte Funktion: aufgerufen über einen Funktionsaufruf.
assert.equal(add(2, 1), 3);Methode: gespeichert in einer Eigenschaft, aufgerufen über einen Methodenaufruf.
const obj = { addAsMethod: add };
assert.equal(obj.addAsMethod(2, 4), 6); // (A)In Zeile A wird obj als Empfänger des Methodenaufrufs bezeichnet.
Konstruktorfunktion: aufgerufen über new.
const inst = new add();
assert.equal(inst instanceof add, true);Nebenbei bemerkt: Die Namen von Konstruktorfunktionen (inkl. Klassen) beginnen normalerweise mit Großbuchstaben.
Die Unterscheidung zwischen den Konzepten Syntax, Entität und Rolle ist subtil und spielt oft keine Rolle. Aber ich möchte Ihr Auge dafür schärfen
Viele andere Programmiersprachen haben nur eine einzige Entität, die die Rolle echte Funktion spielt. Dann können sie den Namen Funktion für Rolle und Entität verwenden.
Spezialisierte Funktionen sind Einzweckversionen von gewöhnlichen Funktionen. Jede von ihnen spezialisiert sich auf eine einzige Rolle
Der Zweck einer Pfeilfunktion ist es, eine echte Funktion zu sein
const arrow = () => {
return 123;
};
assert.equal(arrow(), 123);Der Zweck einer Methode ist es, eine Methode zu sein
const obj = {
myMethod() {
return 'abc';
}
};
assert.equal(obj.myMethod(), 'abc');Der Zweck einer Klasse ist es, eine Konstruktorfunktion zu sein
class MyClass {
/* ··· */
}
const inst = new MyClass();Neben einer schöneren Syntax unterstützt jede Art von spezialisierter Funktion auch neue Features, wodurch sie besser in ihren Aufgaben sind als gewöhnliche Funktionen.
Tabelle 16 listet die Fähigkeiten von gewöhnlichen und spezialisierten Funktionen auf.
| Funktionsaufruf | Methodenaufruf | Konstruktoraufruf | |
|---|---|---|---|
| Gewöhnliche Funktion | (this === undefined) |
✔ |
✔ |
| Pfeilfunktion | ✔ |
(lexikalisches this) |
✘ |
| Methode | (this === undefined) |
✔ |
✘ |
| Klasse | ✘ |
✘ |
✔ |
Es ist wichtig zu beachten, dass Pfeilfunktionen, Methoden und Klassen immer noch als Funktionen kategorisiert werden
> (() => {}) instanceof Function
true
> ({ method() {} }.method) instanceof Function
true
> (class SomeClass {}) instanceof Function
truePfeilfunktionen wurden aus zwei Gründen in JavaScript eingeführt
this auf das Objekt zugreifen, das einen Methodenaufruf empfangen hat. Pfeilfunktionen können auf das this einer umgebenden Methode zugreifen, gewöhnliche Funktionen können das nicht (weil sie ihr eigenes this haben).Wir werden zuerst die Syntax von Pfeilfunktionen untersuchen und dann, wie this in verschiedenen Funktionen funktioniert.
Lassen Sie uns die Syntax eines anonymen Funktionsausdrucks wiederholen
const f = function (x, y, z) { return 123 };Die (ungefähr) äquivalente Pfeilfunktion sieht wie folgt aus. Pfeilfunktionen sind Ausdrücke.
const f = (x, y, z) => { return 123 };Hier ist der Körper der Pfeilfunktion ein Block. Er kann aber auch ein Ausdruck sein. Die folgende Pfeilfunktion funktioniert exakt wie die vorherige.
const f = (x, y, z) => 123;Wenn eine Pfeilfunktion nur einen einzelnen Parameter hat und dieser Parameter ein Bezeichner ist (kein Destrukturierungsmuster), können Sie die Klammern um den Parameter weglassen
const id = x => x;Das ist praktisch, wenn Pfeilfunktionen als Parameter an andere Funktionen oder Methoden übergeben werden
> [1,2,3].map(x => x+1)
[ 2, 3, 4 ]Dieses vorherige Beispiel zeigt einen Vorteil von Pfeilfunktionen – Prägnanz. Wenn wir die gleiche Aufgabe mit einem Funktionsausdruck durchführen, ist unser Code ausführlicher
[1,2,3].map(function (x) { return x+1 });Wenn der Ausdruckskörper einer Pfeilfunktion ein Objektliteral sein soll, müssen Sie das Literal in Klammern setzen
const func1 = () => ({a: 1});
assert.deepEqual(func1(), { a: 1 });Wenn Sie das nicht tun, denkt JavaScript, die Pfeilfunktion habe einen Blockkörper (der nichts zurückgibt)
const func2 = () => {a: 1};
assert.deepEqual(func2(), undefined);{a: 1} wird als Block mit dem Label a: und der Ausdrucksanweisung 1 interpretiert. Ohne eine explizite return-Anweisung gibt der Blockkörper undefined zurück.
Dieser Fallstrick wird durch syntaktische Mehrdeutigkeit verursacht: Objektliterale und Codeblöcke haben die gleiche Syntax. Wir verwenden die Klammern, um JavaScript mitzuteilen, dass der Körper ein Ausdruck (ein Objektliteral) und keine Anweisung (ein Block) ist.
this in Methoden, gewöhnlichen Funktionen und Pfeilfunktionen Die spezielle Variable
this ist ein objektorientiertes Feature
Wir werfen hier einen kurzen Blick auf die spezielle Variable this, um zu verstehen, warum Pfeilfunktionen bessere echte Funktionen sind als gewöhnliche Funktionen.
Aber dieses Feature ist nur in der objektorientierten Programmierung wichtig und wird ausführlicher in §28.5 „Methoden und die spezielle Variable this“ behandelt. Machen Sie sich daher keine Sorgen, wenn Sie es noch nicht vollständig verstehen.
Innerhalb von Methoden ermöglicht uns die spezielle Variable this den Zugriff auf den Empfänger – das Objekt, das den Methodenaufruf erhalten hat
const obj = {
myMethod() {
assert.equal(this, obj);
}
};
obj.myMethod();Gewöhnliche Funktionen können Methoden sein und haben daher auch den impliziten Parameter this
const obj = {
myMethod: function () {
assert.equal(this, obj);
}
};
obj.myMethod();this ist sogar ein impliziter Parameter, wenn wir eine gewöhnliche Funktion als echte Funktion verwenden. Dann ist ihr Wert undefined (wenn Strict Mode aktiv ist, was fast immer der Fall ist)
function ordinaryFunc() {
assert.equal(this, undefined);
}
ordinaryFunc();Das bedeutet, dass eine gewöhnliche Funktion, die als echte Funktion verwendet wird, nicht auf das this einer umgebenden Methode zugreifen kann (Zeile A). Im Gegensatz dazu haben Pfeilfunktionen kein this als impliziten Parameter. Sie behandeln es wie jede andere Variable und können daher auf das this einer umgebenden Methode zugreifen (Zeile B)
const jill = {
name: 'Jill',
someMethod() {
function ordinaryFunc() {
assert.throws(
() => this.name, // (A)
/^TypeError: Cannot read properties of undefined \(reading 'name'\)$/);
}
ordinaryFunc();
const arrowFunc = () => {
assert.equal(this.name, 'Jill'); // (B)
};
arrowFunc();
},
};
jill.someMethod();In diesem Code können wir zwei Arten der Behandlung von this beobachten
Dynamisches this: In Zeile A versuchen wir, auf das this von .someMethod() aus einer gewöhnlichen Funktion zuzugreifen. Dort wird es von der eigenen this der Funktion überschattet, die undefined ist (wie sie vom Funktionsaufruf zugewiesen wird). Da gewöhnliche Funktionen ihr this über (dynamische) Funktions- oder Methodenaufrufe erhalten, wird ihr this als dynamisch bezeichnet.
Lexikalisches this: In Zeile B versuchen wir erneut, auf das this von .someMethod() zuzugreifen. Diesmal gelingt es uns, weil die Pfeilfunktion kein eigenes this hat. this wird lexikalisch aufgelöst, genau wie jede andere Variable. Deshalb wird das this von Pfeilfunktionen als lexikalisch bezeichnet.
Normalerweise sollten Sie spezialisierte Funktionen gegenüber gewöhnlichen Funktionen bevorzugen, insbesondere Klassen und Methoden.
Wenn es um echte Funktionen geht, ist die Wahl zwischen einer Pfeilfunktion und einer gewöhnlichen Funktion weniger eindeutig
Für anonyme Inline-Funktionsausdrücke sind Pfeilfunktionen klare Gewinner, aufgrund ihrer kompakten Syntax und weil sie kein this als impliziten Parameter haben
const twiceOrdinary = [1, 2, 3].map(function (x) {return x * 2});
const twiceArrow = [1, 2, 3].map(x => x * 2);Für eigenständige benannte Funktionsdeklarationen profitieren Pfeilfunktionen immer noch von lexikalischem this. Aber Funktionsdeklarationen (die gewöhnliche Funktionen erzeugen) haben eine schöne Syntax und frühe Aktivierung ist auch gelegentlich nützlich (siehe §11.8 „Deklarationen: Gültigkeitsbereich und Aktivierung“). Wenn this nicht im Körper einer gewöhnlichen Funktion vorkommt, gibt es keinen Nachteil, sie als echte Funktion zu verwenden. Das statische Prüftool ESLint kann uns während der Entwicklung warnen, wenn wir dies über eine integrierte Regel falsch machen.
function timesOrdinary(x, y) {
return x * y;
}
const timesArrow = (x, y) => {
return x * y;
}; Dieser Abschnitt bezieht sich auf zukünftige Inhalte
Dieser Abschnitt dient hauptsächlich als Referenz für die aktuellen und kommenden Kapitel. Machen Sie sich keine Sorgen, wenn Sie nicht alles verstehen.
Bisher waren alle (echten) Funktionen und Methoden, die wir gesehen haben
Spätere Kapitel werden andere Programmiermodi behandeln
Diese Modi können kombiniert werden – zum Beispiel gibt es synchrone Iterables und asynchrone Iterables.
Mehrere neue Arten von Funktionen und Methoden helfen bei einigen der Moduskombinationen
Das lässt uns mit 4 Arten (2 × 2) von Funktionen und Methoden
Tabelle 17 gibt einen Überblick über die Syntax zum Erstellen dieser 4 Arten von Funktionen und Methoden.
| Ergebnis | # | ||
|---|---|---|---|
| Synchrone Funktion | Synchrone Methode | ||
function f() {} |
{ m() {} } |
Wert | 1 |
f = function () {} |
|||
f = () => {} |
|||
| Synchrone Generatorfunktion | Synchrone Generator-Methode | ||
function* f() {} |
{ * m() {} } |
Iterable | 0+ |
f = function* () {} |
|||
| Async-Funktion | Async-Methode | ||
async function f() {} |
{ async m() {} } |
Promise | 1 |
f = async function () {} |
|||
f = async () => {} |
|||
| Asynchrone Generatorfunktion | Asynchrone Generator-Methode | ||
async function* f() {} |
{ async * m() {} } |
Async-Iterable | 0+ |
f = async function* () {} |
(Alles, was in diesem Abschnitt erwähnt wird, gilt sowohl für Funktionen als auch für Methoden.)
Die return-Anweisung gibt explizit einen Wert aus einer Funktion zurück
function func() {
return 123;
}
assert.equal(func(), 123);Ein weiteres Beispiel
function boolToYesNo(bool) {
if (bool) {
return 'Yes';
} else {
return 'No';
}
}
assert.equal(boolToYesNo(true), 'Yes');
assert.equal(boolToYesNo(false), 'No');Wenn Sie am Ende einer Funktion nichts explizit zurückgegeben haben, gibt JavaScript für Sie undefined zurück
function noReturn() {
// No explicit return
}
assert.equal(noReturn(), undefined);Auch hier erwähne ich nur Funktionen in diesem Abschnitt, aber alles gilt auch für Methoden.
Der Begriff Parameter und der Begriff Argument bedeuten im Grunde dasselbe. Wenn Sie möchten, können Sie die folgende Unterscheidung treffen
Parameter sind Teil einer Funktionsdefinition. Sie werden auch als formale Parameter und formale Argumente bezeichnet.
Argumente sind Teil eines Funktionsaufrufs. Sie werden auch als aktuelle Parameter und aktuelle Argumente bezeichnet.
Ein Callback oder Callback-Funktion ist eine Funktion, die ein Argument eines Funktions- oder Methodenaufrufs ist.
Das Folgende ist ein Beispiel für einen Callback
const myArray = ['a', 'b'];
const callback = (x) => console.log(x);
myArray.forEach(callback);
// Output:
// 'a'
// 'b'JavaScript beschwert sich nicht, wenn ein Funktionsaufruf eine andere Anzahl von Argumenten liefert als von der Funktionsdefinition erwartet
undefined gesetzt.Zum Beispiel
function foo(x, y) {
return [x, y];
}
// Too many arguments:
assert.deepEqual(foo('a', 'b', 'c'), ['a', 'b']);
// The expected number of arguments:
assert.deepEqual(foo('a', 'b'), ['a', 'b']);
// Not enough arguments:
assert.deepEqual(foo('a'), ['a', undefined]);Parameter-Standardwerte legen den zu verwendenden Wert fest, wenn ein Parameter nicht bereitgestellt wurde – zum Beispiel
function f(x, y=0) {
return [x, y];
}
assert.deepEqual(f(1), [1, 0]);
assert.deepEqual(f(), [undefined, 0]);undefined löst ebenfalls den Standardwert aus
assert.deepEqual(
f(undefined, undefined),
[undefined, 0]);Ein Restparameter wird deklariert, indem einem Bezeichner drei Punkte (...) vorangestellt werden. Während eines Funktions- oder Methodenaufrufs empfängt er ein Array mit allen verbleibenden Argumenten. Wenn am Ende keine zusätzlichen Argumente vorhanden sind, ist es ein leeres Array – zum Beispiel
function f(x, ...y) {
return [x, y];
}
assert.deepEqual(
f('a', 'b', 'c'), ['a', ['b', 'c']]
);
assert.deepEqual(
f(), [undefined, []]
);Es gibt zwei Einschränkungen hinsichtlich der Verwendung von Restparametern
Wir können nicht mehr als einen Restparameter pro Funktionsdefinition verwenden.
assert.throws(
() => eval('function f(...x, ...y) {}'),
/^SyntaxError: Rest parameter must be last formal parameter$/
);Ein Restparameter muss immer zuletzt kommen. Infolgedessen können wir den letzten Parameter nicht so zugreifen
assert.throws(
() => eval('function f(...restParams, lastParam) {}'),
/^SyntaxError: Rest parameter must be last formal parameter$/
);Sie können einen Restparameter verwenden, um eine bestimmte Anzahl von Argumenten zu erzwingen. Nehmen Sie zum Beispiel die folgende Funktion
function createPoint(x, y) {
return {x, y};
// same as {x: x, y: y}
}So zwingen wir Aufrufer, immer zwei Argumente bereitzustellen
function createPoint(...args) {
if (args.length !== 2) {
throw new Error('Please provide exactly 2 arguments!');
}
const [x, y] = args; // (A)
return {x, y};
}In Zeile A greifen wir über Destrukturierung auf die Elemente von args zu.
Wenn jemand eine Funktion aufruft, werden die vom Aufrufer bereitgestellten Argumente den vom Aufgerufenen empfangenen Parametern zugewiesen. Zwei gängige Möglichkeiten, die Zuordnung durchzuführen, sind
Positionsbasierte Parameter: Ein Argument wird einem Parameter zugewiesen, wenn sie die gleiche Position haben. Ein Funktionsaufruf nur mit positionsbasierten Argumenten sieht wie folgt aus.
selectEntries(3, 20, 2)Benannte Parameter: Ein Argument wird einem Parameter zugewiesen, wenn sie den gleichen Namen haben. JavaScript hat keine benannten Parameter, aber Sie können sie simulieren. Zum Beispiel ist dies ein Funktionsaufruf nur mit (simulierten) benannten Argumenten
selectEntries({start: 3, end: 20, step: 2})Benannte Parameter haben mehrere Vorteile
Sie führen zu selbsterklärenderem Code, da jedes Argument eine beschreibende Bezeichnung hat. Vergleichen Sie einfach die beiden Versionen von selectEntries(): Mit der zweiten ist leichter zu erkennen, was passiert.
Die Reihenfolge der Argumente spielt keine Rolle (solange die Namen korrekt sind).
Die Handhabung von mehr als einem optionalen Parameter ist bequemer: Aufrufer können leicht jede Teilmenge aller optionalen Parameter bereitstellen und müssen sich nicht um die weggelassenen kümmern (bei positionsbasierten Parametern müssen Sie vorherige optionale Parameter mit undefined auffüllen).
JavaScript hat keine echten benannten Parameter. Der offizielle Weg, sie zu simulieren, sind Objektliterale
function selectEntries({start=0, end=-1, step=1}) {
return {start, end, step};
}Diese Funktion verwendet Destrukturierung, um auf die Eigenschaften ihres einzelnen Parameters zuzugreifen. Das von ihr verwendete Muster ist eine Abkürzung für das folgende Muster
{start: start=0, end: end=-1, step: step=1}Dieses Destrukturierungsmuster funktioniert für leere Objektliterale
> selectEntries({})
{ start: 0, end: -1, step: 1 }Aber es funktioniert nicht, wenn Sie die Funktion ohne Argumente aufrufen
> selectEntries()
TypeError: Cannot read properties of undefined (reading 'start')Sie können dies beheben, indem Sie einen Standardwert für das gesamte Muster bereitstellen. Dieser Standardwert funktioniert genauso wie Standardwerte für einfachere Parameterdefinitionen: Wenn der Parameter fehlt, wird der Standard verwendet.
function selectEntries({start=0, end=-1, step=1} = {}) {
return {start, end, step};
}
assert.deepEqual(
selectEntries(),
{ start: 0, end: -1, step: 1 });...) in FunktionsaufrufenWenn Sie drei Punkte (...) vor das Argument eines Funktionsaufrufs setzen, sprechen Sie es. Das bedeutet, dass das Argument ein Iterable-Objekt sein muss und die iterierten Werte alle zu Argumenten werden. Mit anderen Worten, ein einzelnes Argument wird in mehrere Argumente erweitert – zum Beispiel
function func(x, y) {
console.log(x);
console.log(y);
}
const someIterable = ['a', 'b'];
func(...someIterable);
// same as func('a', 'b')
// Output:
// 'a'
// 'b'Spread-Argumente und Restparameter verwenden die gleiche Syntax (...), dienen aber gegensätzlichen Zwecken
Math.max()Math.max() gibt die größte seiner null oder mehr Argumente zurück. Leider kann es nicht für Arrays verwendet werden, aber Spread gibt uns einen Ausweg
> Math.max(-1, 5, 11, 3)
11
> Math.max(...[-1, 5, 11, 3])
11
> Math.max(-1, ...[-5, 11], 3)
11Array.prototype.push()Ähnlich fügt die Array-Methode .push() destruktiv ihre null oder mehr Parameter am Ende ihres Arrays hinzu. JavaScript hat keine Methode zum destruktiven Anhängen eines Arrays an ein anderes. Wieder einmal werden wir durch Spread gerettet
const arr1 = ['a', 'b'];
const arr2 = ['c', 'd'];
arr1.push(...arr2);
assert.deepEqual(arr1, ['a', 'b', 'c', 'd']); Übungen: Parameterbehandlung
exercises/callables/positional_parameters_test.mjsexercises/callables/named_parameters_test.mjs.call(), .apply(), .bind()Funktionen sind Objekte und haben Methoden. In diesem Abschnitt befassen wir uns mit drei dieser Methoden: .call(), .apply() und .bind().
.call()Jede Funktion someFunc hat die folgende Methode
someFunc.call(thisValue, arg1, arg2, arg3);Dieser Methodenaufruf ist lose äquivalent zum folgenden Funktionsaufruf
someFunc(arg1, arg2, arg3);Mit .call() können wir jedoch auch einen Wert für den impliziten Parameter this angeben. Mit anderen Worten: .call() macht den impliziten Parameter this explizit.
Der folgende Code demonstriert die Verwendung von .call()
function func(x, y) {
return [this, x, y];
}
assert.deepEqual(
func.call('hello', 'a', 'b'),
['hello', 'a', 'b']);Wie wir bereits gesehen haben, ist bei einem Funktionsaufruf einer gewöhnlichen Funktion ihr this undefined
assert.deepEqual(
func('a', 'b'),
[undefined, 'a', 'b']);Daher ist der vorherige Funktionsaufruf äquivalent zu
assert.deepEqual(
func.call(undefined, 'a', 'b'),
[undefined, 'a', 'b']);In Pfeilfunktionen wird der Wert für this, der über .call() (oder andere Mittel) bereitgestellt wird, ignoriert.
.apply()Jede Funktion someFunc hat die folgende Methode
someFunc.apply(thisValue, [arg1, arg2, arg3]);Dieser Methodenaufruf ist lose äquivalent zum folgenden Funktionsaufruf (der Spreading verwendet)
someFunc(...[arg1, arg2, arg3]);Mit .apply() können wir jedoch auch einen Wert für den impliziten Parameter this angeben.
Der folgende Code demonstriert die Verwendung von .apply()
function func(x, y) {
return [this, x, y];
}
const args = ['a', 'b'];
assert.deepEqual(
func.apply('hello', args),
['hello', 'a', 'b']);.bind().bind() ist eine weitere Methode von Funktions-Objekten. Diese Methode wird wie folgt aufgerufen
const boundFunc = someFunc.bind(thisValue, arg1, arg2);.bind() gibt eine neue Funktion boundFunc() zurück. Der Aufruf dieser Funktion ruft someFunc() mit this auf thisValue und diesen Parametern gesetzt auf: arg1, arg2, gefolgt von den Parametern von boundFunc().
Das heißt, die folgenden beiden Funktionsaufrufe sind äquivalent
boundFunc('a', 'b')
someFunc.call(thisValue, arg1, arg2, 'a', 'b').bind()Eine weitere Möglichkeit, this und Parameter vorab zu füllen, ist über eine Pfeilfunktion
const boundFunc2 = (...args) =>
someFunc.call(thisValue, arg1, arg2, ...args);.bind()Unter Berücksichtigung des vorherigen Abschnitts kann .bind() als echte Funktion wie folgt implementiert werden
function bind(func, thisValue, ...boundArgs) {
return (...args) =>
func.call(thisValue, ...boundArgs, ...args);
}Die Verwendung von .bind() für echte Funktionen ist etwas unintuitiv, da wir einen Wert für this angeben müssen. Da dieser bei Funktionsaufrufen undefined ist, wird er normalerweise auf undefined oder null gesetzt.
Im folgenden Beispiel erstellen wir add8(), eine Funktion mit einem Parameter, indem wir den ersten Parameter von add() an 8 binden.
function add(x, y) {
return x + y;
}
const add8 = add.bind(undefined, 8);
assert.equal(add8(1), 9); Quiz
Siehe Quiz-App.