Mit bind, call und apply den Kontext in JavaScript meistern

In meinem letzten Beitrag habe ich bewusst noch ein paar Dinge unterschlagen, die im Umgang mit this hilfreich sein können: bind, apply und call. Zum einen war der Artikel schon gefühlt lang genug und außerdem nutze ich die drei Methoden (so gut wie) nie. Da mich Tobias aber darauf hingewiesen hat, hole ich das Ganze hier nach.

Einer Methode einen Context zuweisen, mit bind

Bisher habe ich, wenn ich einer Funktion einen Kontext mitgeben wollen, ein wenig geschummelt. In meinem letzten Artikel habe ich this in einer anderen Variable gespeichert und diese dann in der Funktion genutzt.

Sehen wir uns das Beispiel aus dem letzten Artikel nochmal mit einer kleinen Veränderung an und versuchen dann, ohne that zu arbeiten:

const john = {
lyrics: 'Let is be, let it be',
sing() {
console.log(this.lyrics);
},
singLater() {
const that = this;
setTimeout(function () {
that.sing();
}, 1);
}
};
john.singLater(); // 🎵 Let is be, let it be

Mithilfe von bind erstellen wir nun eine neue Variable singWithContext und binden in dieser den aktuellen Kontext (this, was hier john ist) an die Funktion sing.

const john = {
lyrics: 'Let is be, let it be',
sing() {
console.log(this.lyrics);
},
singLater() {
const singWithContext = this.sing.bind(this);
setTimeout(function () {
singWithContext();
}, 1);
}
};

john.singLater(); // 🎵 Let is be, let it be
Die Funktion sing wird an this gebunden

Das geht auch noch etwas kürzer:

const john = {
lyrics: 'Let is be, let it be',
sing() {
console.log(this.lyrics);
},
singLater() {
const singWithContext = this.sing.bind(this);
setTimeout(singWithContext, 1);
}
};
john.singLater(); // 🎵 Let is be, let it be

Man kann bind auch noch nutzen, um sich Methoden von anderen Objekten zu „borgen“: Nehmen wir einmal das folgende Beispiel:

const farin = {
firstName: 'Farin',
lastName: 'Urlaub',
fullName: function () {
return `${this.firstName} ${this.lastName}`;
}
};

const bela = {
firstName: 'Bela B',
lastName: 'Felsenheimer'
};

console.log(farin.fullName()); // Farin Urlaub

Mittels bind können wir jetzt den Kontext von Farins fullName() ändern, um auch den Namen von Bela B auszugeben

const farin = {
firstName: 'Farin',
lastName: 'Urlaub',
fullName: function () {
return `${this.firstName} ${this.lastName}`;
}
};

const bela = {
firstName: 'Bela B',
lastName: 'Felsenheimer'
};

console.log(farin.fullName()); // Farin Urlaub
const belaBFullName = farin.fullName.bind(bela);
console.log(belaBFullName()); // Bela B Felsenheimer
Die Funktion fullName aus dem Objekt farin wird an das Objekt bela gebunden

An einem unbekannten Ort ruft jemand jetzt wahrscheinlich: "Das geht auch mit Vererbung!" und diese Person hat auch mehr oder weniger Recht. Wir nutzen hier aber JavaScript und damit eine (halbwegs) funktionale Programmiersprache und diese bietet uns halt Wege, die in anderen Programmiersprachen so nicht existieren.

apply und call

Jetzt ist es stellenweise auch etwas umständlich, die mit bind an einen Kontext gebundene Funktion immer in einer Variable speichern zu müssen.

const rod = {
firstName: 'Rod',
lastName: 'Gonzales'
};

function sing(lyrics) {
console.log(`${this.firstName} ${this.lastName}: ${lyrics}`);
}

const rodSings = sing.bind(rod);

rodSings('Das sind Dinge, von denen ich gar nichts wissen will'); // 🎵 Rod Gonzales: Das sind Dinge, von denen ich gar nichts wissen will

Mit apply und call können wir das etwas verkürzen

const rod = {
firstName: 'Rod',
lastName: 'Gonzales'
};

function sing(lyrics) {
console.log(`${this.firstName} ${this.lastName}: ${lyrics}`);
}

sing.call(rod, 'Das sind Dinge, von denen ich gar nichts wissen will'); // 🎵 Rod Gonzales: Das sind Dinge, von denen ich gar nichts wissen will

Bei call geben wir erst den Kontext an und dann alle Parameter, mit denen wir die Funktion aufrufen wollen.

const rod = {
firstName: 'Rod',
lastName: 'Gonzales'
};

function sing(lyrics) {
return console.log(`${this.firstName} ${this.lastName}: ${lyrics}`);
}

sing.apply(rod, ['Das sind Dinge, von denen ich gar nichts wissen will']); // 🎵 Rod Gonzales: Das sind Dinge, von denen ich gar nichts wissen will

Der Unterschied zu apply ist minimal: apply erwartet als zweiten Parameter einen Array, der alle Parameter für einen Aufruf enthält.

Mit bind, apply und call, haben wir drei Möglichkeiten, den Kontext einer Funktion zu manipulieren. Das ist definitiv einfacher zu lesen als Hilfsvariablen wie that oder self zu nutzen. Ich selbst nutze die drei aber, wie erwähnt, selten. Dies liegt wahrscheinlich daran, dass ich eher funktional programmiere und Objekte bei mir meistens nur Daten enthalten.

Wie sieht Eure Erfahrung hier aus? Schlagt ihr Euch mit dem Kontext in Euren Programmen herum und setzt bind, apply und call ein oder geht es Euch wie mir? Schreibt mir gerne auf Mastodon oder Twitter.