Πώς να κατανοήσετε τη λέξη-κλειδί αυτό και το περιεχόμενο στο JavaScript

Όπως αναφέρθηκε σε ένα από τα προηγούμενα άρθρα μου, η πλήρη χρήση του JavaScript μπορεί να είναι ένα μακρύ ταξίδι. Μπορεί να έχετε συναντήσει thisτο ταξίδι σας ως προγραμματιστής JavaScript. Όταν ξεκίνησα, το είδα για πρώτη φορά κατά τη χρήση eventListenersκαι με το jQuery. Αργότερα, έπρεπε να το χρησιμοποιώ συχνά με το React και είμαι σίγουρος ότι το κάνατε επίσης. Αυτό δεν σημαίνει ότι κατάλαβα πραγματικά τι είναι και πώς να το ελέγξω πλήρως.

Ωστόσο, είναι πολύ χρήσιμο να κυριαρχήσετε την ιδέα πίσω από αυτό, και όταν προσεγγιστεί με καθαρό μυαλό, δεν είναι και πολύ δύσκολο.

Εξετάζοντας αυτό

Η εξήγηση thisμπορεί να οδηγήσει σε μεγάλη σύγχυση, απλώς με την ονομασία της λέξης-κλειδιού.

thisσυνδέεται στενά με το πλαίσιο στο οποίο βρίσκεστε, στο πρόγραμμά σας. Ας ξεκινήσουμε από την αρχή. Στο πρόγραμμα περιήγησής μας, εάν πληκτρολογήσετε απλώς thisτην κονσόλα, θα λάβετε το- windowαντικείμενο, το πιο απομακρυσμένο πλαίσιο για το JavaScript σας. Στο Node.js, αν κάνουμε:

console.log(this)

καταλήγουμε με {}ένα κενό αντικείμενο. Αυτό είναι λίγο περίεργο, αλλά φαίνεται ότι το Node.js συμπεριφέρεται έτσι. Εάν το κάνετε

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

Ωστόσο, θα λάβετε το globalαντικείμενο, το πιο απομακρυσμένο πλαίσιο. Σε αυτό το πλαίσιο setTimeout, setIntervalαποθηκεύονται. Μη διστάσετε να παίξετε λίγο μαζί του για να δείτε τι μπορείτε να κάνετε με αυτό. Από εδώ, δεν υπάρχει σχεδόν καμία διαφορά μεταξύ του Node.js και του προγράμματος περιήγησης. Θα χρησιμοποιώ window. Απλώς θυμηθείτε ότι στο Node.js θα είναι το globalαντικείμενο, αλλά δεν κάνει πραγματικά τη διαφορά.

Θυμηθείτε: Το περιεχόμενο έχει νόημα μόνο στο εσωτερικό των λειτουργιών

Φανταστείτε ότι γράφετε ένα πρόγραμμα χωρίς να τοποθετείτε τίποτα σε συναρτήσεις. Απλά γράφετε μια γραμμή μετά την άλλη, χωρίς να κατεβείτε συγκεκριμένες δομές. Αυτό σημαίνει ότι δεν χρειάζεται να παρακολουθείτε πού βρίσκεστε. Είστε πάντα στο ίδιο επίπεδο.

Όταν αρχίζετε να έχετε συναρτήσεις, ενδέχεται να έχετε διαφορετικά επίπεδα του προγράμματός σας και να thisαντιπροσωπεύει πού βρίσκεστε, ποιο αντικείμενο ονομάζεται συνάρτηση.

Παρακολούθηση του αντικειμένου καλούντος

Ας ρίξουμε μια ματιά στο παρακάτω παράδειγμα και ας δούμε πώς thisαλλάζει ανάλογα με το περιβάλλον:

const coffee = { strong: true, info: function() { console.log(`The coffee is ${this.strong ? '' : 'not '}strong`) }, } coffee.info() // The coffee is strong

Δεδομένου ότι καλούμε μια συνάρτηση που δηλώνεται μέσα στο coffeeαντικείμενο, το περιβάλλον μας αλλάζει ακριβώς σε αυτό το αντικείμενο. Τώρα μπορούμε να έχουμε πρόσβαση σε όλες τις ιδιότητες αυτού του αντικειμένου this. Στο παραπάνω παράδειγμα, θα μπορούσαμε απλώς να το αναφέρουμε απευθείας κάνοντας coffee.strong. Γίνεται πιο ενδιαφέρον, όταν δεν ξέρουμε ποιο πλαίσιο, ποιο αντικείμενο, είμαστε ή όταν τα πράγματα γίνονται λίγο πιο περίπλοκα. Ρίξτε μια ματιά στο ακόλουθο παράδειγμα:

const drinks = [ { name: 'Coffee', addictive: true, info: function() { console.log(`${this.name} is ${this.addictive ? '' : 'not '} addictive.`) }, }, { name: 'Celery Juice', addictive: false, info: function() { console.log(`${this.name} is ${this.addictive ? '' : 'not '} addictive.`) }, }, ] function pickRandom(arr) { return arr[Math.floor(Math.random() * arr.length)] } pickRandom(drinks).info()

Μαθήματα και παρουσίες

Τα μαθήματα μπορούν να χρησιμοποιηθούν για την αφαίρεση του κώδικα και την κοινή χρήση συμπεριφοράς. Η επανάληψη της infoδήλωσης λειτουργίας στο τελευταίο παράδειγμα δεν είναι καλή. Δεδομένου ότι τα μαθήματα και οι παρουσίες τους είναι στην πραγματικότητα αντικείμενα, συμπεριφέρονται με τον ίδιο τρόπο. Ένα πράγμα που πρέπει να σημειωθεί, είναι ότι η δήλωση thisστον κατασκευαστή είναι στην πραγματικότητα μια πρόβλεψη για το μέλλον, όταν θα υπάρξει μια παρουσία.

Ας ΡΙΞΟΥΜΕ μια ΜΑΤΙΑ:

class Coffee { constructor(strong) { this.strong = !!strong } info() { console.log(`This coffee is ${this.strong ? '' : 'not '}strong`) } } const strongCoffee = new Coffee(true) const normalCoffee = new Coffee(false) strongCoffee.info() // This coffee is strong normalCoffee.info() // This coffee is not strong

Pitfall: απρόσκοπτη ένθετη κλήση λειτουργίας

Μερικές φορές, καταλήγουμε σε ένα πλαίσιο που δεν περιμέναμε πραγματικά. Αυτό μπορεί να συμβεί, όταν καλούμε άγνωστα τη συνάρτηση μέσα σε ένα άλλο αντικείμενο αντικειμένου. Ένα πολύ κοινό παράδειγμα είναι όταν χρησιμοποιείτε setTimeoutή setInterval:

// BAD EXAMPLE const coffee = { strong: true, amount: 120, drink: function() { setTimeout(function() { if (this.amount) this.amount -= 10 }, 10) }, } coffee.drink()

Τι νομίζετε ότι coffee.amountείναι;

...

..

.

Είναι ακόμα 120. Πρώτα, ήμασταν μέσα στο coffeeαντικείμενο, καθώς η drinkμέθοδος δηλώνεται μέσα σε αυτό. Απλά κάναμε setTimeoutκαι τίποτα άλλο. Αυτό είναι ακριβώς.

Όπως εξήγησα νωρίτερα, η setTimeoutμέθοδος δηλώνεται στην πραγματικότητα στο windowαντικείμενο. Όταν το καλούμε, αλλάζουμε το περιεχόμενο στο windowξανά. Αυτό σημαίνει ότι οι οδηγίες μας στην πραγματικότητα προσπάθησαν να αλλάξουν window.amount, αλλά κατέληξε να μην κάνει τίποτα λόγω της ifδήλωσης. Για να το φροντίσουμε αυτό, πρέπει να bindλειτουργήσουμε (βλ. Παρακάτω).

Αντιδρώ

Χρησιμοποιώντας το React, ελπίζουμε ότι θα είναι κάτι παρελθόν σύντομα, χάρη στον Hooks. Προς το παρόν, πρέπει να κάνουμε τα bindπάντα (περισσότερα σε αυτό αργότερα) με τον ένα ή τον άλλο τρόπο. Όταν ξεκίνησα, δεν είχα ιδέα γιατί το έκανα, αλλά σε αυτό το σημείο, πρέπει να γνωρίζετε ήδη γιατί είναι απαραίτητο.

Ας ρίξουμε μια ματιά σε δύο απλά συστατικά κλάσης React:

// BAD EXAMPLE import React from 'react' class Child extends React.Component { render() { return  Get some Coffee!  } } class Parent extends React.Component { constructor(props) { super(props) this.state = { coffeeCount: 0, } // change to turn into good example – normally we would do: // this._getCoffee = this._getCoffee.bind(this) } render() { return (    ) } _getCoffee() { this.setState({ coffeeCount: this.state.coffeeCount + 1, }) } }

Όταν κάνουμε τώρα κλικ στο κουμπί που αποδίδεται από το Child, θα λάβουμε ένα σφάλμα. Γιατί; Επειδή το React άλλαξε το περιβάλλον μας κατά την κλήση της _getCoffeeμεθόδου.

Υποθέτω ότι το React καλεί τη μέθοδο απόδοσης των συστατικών μας σε άλλο πλαίσιο, μέσω τάξεων βοηθών ή παρόμοιων (παρόλο που θα έπρεπε να σκάψω βαθύτερα για να μάθω σίγουρα). Επομένως, this.stateείναι απροσδιόριστο και προσπαθούμε να αποκτήσουμε πρόσβαση this.state.coffeeCount. Θα πρέπει να λάβετε κάτι σαν Cannot read property coffeeCount of undefined.

Για να επιλύσετε το ζήτημα, πρέπει bind(θα φτάσουμε εκεί) στις μεθόδους στις τάξεις μας, μόλις τις περάσουμε από το στοιχείο στο οποίο έχουν οριστεί.

Ας ρίξουμε μια ματιά σε ένα ακόμη γενικό παράδειγμα:

// BAD EXAMPLE class Viking { constructor(name) { this.name = name } prepareForBattle(increaseCount) { console.log(`I am ${this.name}! Let's go fighting!`) increaseCount() } } class Battle { constructor(vikings) { this.vikings = vikings this.preparedVikingsCount = 0 this.vikings.forEach(viking => { viking.prepareForBattle(this.increaseCount) }) } increaseCount() { this.preparedVikingsCount++ console.log(`${this.preparedVikingsCount} vikings are now ready to fight!`) } } const vikingOne = new Viking('Olaf') const vikingTwo = new Viking('Odin') new Battle([vikingOne, vikingTwo])

Περνάμε increaseCountαπό το ένα μάθημα στο άλλο. Όταν καλούμε τη increaseCountμέθοδο μέσα Viking, έχουμε ήδη αλλάξει το περιβάλλον και thisστην πραγματικότητα το επισημαίνουμε Viking, πράγμα που σημαίνει ότι η increaseCountμέθοδος μας δεν θα λειτουργήσει όπως αναμενόταν.

Λύση - δέσμευση

Η απλούστερη λύση για εμάς είναι bindοι μέθοδοι που θα περάσουν από το αρχικό αντικείμενο ή την τάξη μας. Υπάρχουν διαφορετικοί τρόποι με τους οποίους μπορείτε να συνδέσετε συναρτήσεις, αλλά ο πιο συνηθισμένος (επίσης στο React) είναι να το συνδέσετε στον κατασκευαστή. Επομένως, θα πρέπει να προσθέσουμε αυτήν τη γραμμή στον Battleκατασκευαστή πριν από τη γραμμή 18:

this.increaseCount = this.increaseCount.bind(this)

Μπορείτε να συνδέσετε οποιαδήποτε λειτουργία σε οποιοδήποτε περιβάλλον. Αυτό δεν σημαίνει ότι πρέπει πάντα να συνδέσετε τη συνάρτηση με το πλαίσιο στο οποίο δηλώνεται (αυτή είναι η πιο κοινή περίπτωση, ωστόσο). Αντ 'αυτού, θα μπορούσατε να το συνδέσετε σε άλλο πλαίσιο. Με bind, ορίζετε πάντα το πλαίσιο για μια δήλωση συνάρτησης . Αυτό σημαίνει ότι όλες οι κλήσεις για αυτήν τη λειτουργία θα λάβουν το δεσμευμένο περιβάλλον ως this. Υπάρχουν δύο άλλοι βοηθοί για τον καθορισμό του περιβάλλοντος.

Οι λειτουργίες βέλους "() => {}" συνδέουν αυτόματα τη συνάρτηση με το περιβάλλον δήλωσης

Εφαρμόστε και καλέστε

Και οι δύο κάνουν βασικά το ίδιο πράγμα, ακριβώς ότι η σύνταξη είναι διαφορετική. Και για τα δύο, περνάτε το πλαίσιο ως πρώτο όρισμα. applyπαίρνει έναν πίνακα για τα άλλα ορίσματα, με το οποίο callμπορείτε να διαχωρίσετε άλλα ορίσματα με κόμμα. Τώρα τι κάνουν; Και οι δύο αυτές μέθοδοι ορίζουν το πλαίσιο για μια συγκεκριμένη κλήση λειτουργίας . Όταν καλείτε τη συνάρτηση χωρίς call, το περιβάλλον ορίζεται στο προεπιλεγμένο περιβάλλον (ή ακόμη και σε δεσμευμένο περιβάλλον). Εδώ είναι ένα παράδειγμα:

class Salad { constructor(type) { this.type = type } } function showType() { console.log(`The context's type is ${this.type}`) } const fruitSalad = new Salad('fruit') const greekSalad = new Salad('greek') showType.call(fruitSalad) // The context's type is fruit showType.call(greekSalad) // The context's type is greek showType() // The context's type is undefined

Μπορείτε να μαντέψετε ποιο είναι το πλαίσιο της τελευταίας showType()κλήσης;

..

.

Έχεις δίκιο, αυτό είναι το εξωτερικό πεδίο, window. Ως εκ τούτου, typeείναι undefined, δεν υπάρχειwindow.type

Αυτό είναι, ας ελπίσουμε ότι έχετε τώρα μια σαφή κατανόηση σχετικά με τον τρόπο χρήσης thisσε JavaScript. Μη διστάσετε να αφήσετε προτάσεις για το επόμενο άρθρο στα σχόλια.

Σχετικά με τον συγγραφέα: Ο Lukas Gisder-Dubé συν-ίδρυσε και ηγήθηκε μιας εκκίνησης ως CTO για 1 1/2 χρόνια, οικοδομώντας την τεχνολογική ομάδα και την αρχιτεκτονική. Αφού αποχώρησε από την εκκίνηση, δίδαξε κωδικοποίηση ως Lead Instructor στο Ironhack και τώρα δημιουργεί ένα Startup Agency & Consultancy στο Βερολίνο. Ρίξτε μια ματιά στο dube.io για να μάθετε περισσότερα.