JavaScript είναι μια περίεργη γλώσσα. Αν και εμπνέεται από το Smalltalk, χρησιμοποιεί μια σύνταξη τύπου C. Συνδυάζει πτυχές διαδικαστικών, λειτουργικών και αντικειμενοστρεφών προγραμματισμών (OOP) παραδειγμάτων. Εχει πολυάριθμος , συχνά περιττές, προσεγγίσεις για την επίλυση σχεδόν οποιουδήποτε πιθανού προβλήματος προγραμματισμού και δεν έχει έντονη γνώμη για το ποια προτιμώνται. Είναι αδύναμα και δυναμικά δακτυλογραφημένα, με μια μαζική προσέγγιση για τον εξαναγκασμό τύπου που οδηγεί ακόμη και σε έμπειρους προγραμματιστές.
Το JavaScript έχει επίσης κονδυλώματα, παγίδες και αμφισβητήσιμα χαρακτηριστικά. Οι νέοι προγραμματιστές αγωνίζονται με μερικές από τις πιο δύσκολες έννοιες του - σκεφτείτε ασύγχρονο, κλείσιμο και ανύψωση. Οι προγραμματιστές με εμπειρία σε άλλες γλώσσες θεωρούν λογικά ότι τα πράγματα με παρόμοια ονόματα και εμφανίσεις θα λειτουργούν με τον ίδιο τρόπο στο JavaScript και συχνά είναι λάθος. Οι πίνακες δεν είναι πραγματικά πίνακες. ποια είναι η συμφωνία με this
, τι είναι το πρωτότυπο και τι κάνει new
πραγματικά;
Ο χειρότερος παραβάτης είναι μακράν νέος στην τελευταία έκδοση του JavaScript, ECMAScript 6 (ES6): τάξεις . Μερικές από τις συζητήσεις γύρω από τα μαθήματα είναι ειλικρινά ανησυχητικές και αποκαλύπτει μια βαθιά ριζωμένη παρανόηση για το πώς λειτουργεί η γλώσσα:
«Το JavaScript είναι επιτέλους ένα πραγματικός αντικειμενοστραφής γλώσσα τώρα που έχει μαθήματα! '
Ή:
'Τα μαθήματα μας απελευθερώνουν από το να σκεφτόμαστε το σπασμένο μοντέλο κληρονομιάς της JavaScript.'
Ή ακόμη και:
'Τα μαθήματα είναι μια ασφαλέστερη, ευκολότερη προσέγγιση για τη δημιουργία τύπων σε JavaScript.'
Αυτές οι δηλώσεις δεν με ενοχλούν, επειδή υπονοούν ότι υπάρχει κάποιο πρόβλημα πρωτότυπη κληρονομιά ; ας αναιρέσουμε αυτά τα επιχειρήματα. Αυτές οι δηλώσεις με ενοχλούν διότι καμία από αυτές δεν είναι αληθινές και καταδεικνύουν τις συνέπειες της προσέγγισης του JavaScript «όλα για όλους» στη σχεδίαση γλωσσών: μειώνει την κατανόηση της γλώσσας από έναν προγραμματιστή πιο συχνά από ό, τι επιτρέπει. Προτού προχωρήσω περισσότερο, ας το δείξουμε.
function PrototypicalGreeting(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } PrototypicalGreeting.prototype.greet = function() { return `${this.greeting}, ${this.name}!` } const greetProto = new PrototypicalGreeting('Hey', 'folks') console.log(greetProto.greet())
class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } greet() { return `${this.greeting}, ${this.name}!` } } const classyGreeting = new ClassicalGreeting('Hey', 'folks') console.log(classyGreeting.greet())
Η απάντηση εδώ είναι δεν υπάρχει . Αυτά κάνουν το ίδιο πράγμα, είναι μόνο το ερώτημα εάν χρησιμοποιήθηκε η σύνταξη κλάσης ES6.
Ποια από τις παρακάτω αρχές σχεδίασης σας επιτρέπει να ενοποιήσετε τη σελίδα;
Είναι αλήθεια ότι το δεύτερο παράδειγμα είναι πιο εκφραστικό. Μόνο για αυτόν τον λόγο, μπορείτε να υποστηρίξετε ότι class
είναι μια ωραία προσθήκη στη γλώσσα. Δυστυχώς, το πρόβλημα είναι λίγο πιο λεπτό.
function Proto() { this.name = 'Proto' return this; } Proto.prototype.getName = function() { return this.name } class MyClass extends Proto { constructor() { super() this.name = 'MyClass' } } const instance = new MyClass() console.log(instance.getName()) Proto.prototype.getName = function() { return 'Overridden in Proto' } console.log(instance.getName()) MyClass.prototype.getName = function() { return 'Overridden in MyClass' } console.log(instance.getName()) instance.getName = function() { return 'Overridden in instance' } console.log(instance.getName())
Η σωστή απάντηση είναι ότι εκτυπώνει στην κονσόλα:
> MyClass > Overridden in Proto > Overridden in MyClass > Overridden in instance
Αν δεν απαντήσατε σωστά, δεν καταλαβαίνετε τι class
στην πραγματικότητα είναι. Αυτό δεν είναι δικό σας λάθος. Μοιάζει πολύ Array
, class
δεν είναι γλωσσικό χαρακτηριστικό, είναι συντακτικό σκοταδισμό . Προσπαθεί να κρύψει το πρωτότυπο μοντέλο κληρονομιάς και τα αδέξια ιδιώματα που συνοδεύουν, και υπονοεί ότι η JavaScript κάνει κάτι που δεν είναι.
Ίσως σας είπαν ότι class
εισήχθη στο JavaScript για να κάνει τους κλασικούς προγραμματιστές OOP που προέρχονται από γλώσσες όπως η Java πιο άνετα με το μοντέλο κληρονομιάς κλάσης ES6. Αν εσύ είναι ένας από αυτούς τους προγραμματιστές, αυτό το παράδειγμα πιθανότατα σας τρόμαξε. Θα έπρεπε. Δείχνει ότι η JavaScript class
Η λέξη-κλειδί δεν συνοδεύεται από καμία από τις εγγυήσεις που πρέπει να παρέχει μια τάξη. Δείχνει επίσης μια από τις βασικές διαφορές στο μοντέλο κληρονομιάς του πρωτοτύπου: Τα πρωτότυπα είναι εμφανίσεις αντικειμένων , δεν τύποι .
Η πιο σημαντική διαφορά μεταξύ της κληρονομιάς που βασίζεται σε τάξη και πρωτότυπο είναι ότι μια τάξη ορίζει ένα τύπος το οποίο μπορεί να τεκμηριωθεί κατά το χρόνο εκτέλεσης, ενώ ένα πρωτότυπο είναι το ίδιο αντικείμενο παρουσίας.
Ένα παιδί της τάξης ES6 είναι άλλο τύπος ορισμός που επεκτείνει τον γονέα με νέες ιδιότητες και μεθόδους, οι οποίοι με τη σειρά τους μπορούν να τεκμηριωθούν κατά το χρόνο εκτέλεσης. Ένα παιδί ενός πρωτοτύπου είναι ένα άλλο αντικείμενο παράδειγμα που μεταβιβάζει στον γονέα τυχόν ιδιότητες που δεν εφαρμόζονται στο παιδί.
Συμπληρωματική σημείωση: Ίσως αναρωτιέστε γιατί ανέφερα μεθόδους κατηγορίας, αλλά όχι μεθόδους πρωτοτύπου. Αυτό συμβαίνει επειδή το JavaScript δεν έχει μια έννοια μεθόδων. Οι λειτουργίες είναι πρώτη τάξη σε JavaScript και μπορεί να έχουν ιδιότητες ή ιδιότητες άλλων αντικειμένων.
Ένας κατασκευαστής τάξεων δημιουργεί μια παρουσία της τάξης. Ένας κατασκευαστής σε JavaScript είναι απλώς μια παλιά παλιά συνάρτηση που επιστρέφει ένα αντικείμενο. Το μόνο ιδιαίτερο πράγμα για έναν κατασκευαστή JavaScript είναι ότι, όταν καλείται με το new
λέξη-κλειδί, εκχωρεί το πρωτότυπο ως πρωτότυπο του αντικειμένου που επιστρέφεται. Εάν αυτό σας φαίνεται λίγο μπερδεμένο, δεν είστε μόνοι - είναι και είναι μεγάλο μέρος του γιατί τα πρωτότυπα δεν κατανοούνται καλά.
Για να θέσω ένα πολύ καλό σημείο σε αυτό, ένα παιδί ενός πρωτοτύπου δεν είναι αντίγραφο του πρωτοτύπου του, ούτε είναι ένα αντικείμενο με το ίδιο σχήμα ως το πρωτότυπο του. Το παιδί έχει ζωντανή αναφορά προς το το πρωτότυπο και οποιαδήποτε ιδιότητα πρωτοτύπου που δεν υπάρχει στο παιδί είναι μονόδρομη αναφορά σε μια ιδιότητα με το ίδιο όνομα στο πρωτότυπο.
Σκέψου τα ακόλουθα:
let parent = { foo: 'foo' } let child = { } Object.setPrototypeOf(child, parent) console.log(child.foo) // 'foo' child.foo = 'bar' console.log(child.foo) // 'bar' console.log(parent.foo) // 'foo' delete child.foo console.log(child.foo) // 'foo' parent.foo = 'baz' console.log(child.foo) // 'baz'
Σημείωση: Σχεδόν ποτέ δεν θα γράφατε κώδικα όπως αυτό στην πραγματική ζωή - είναι φοβερή πρακτική - αλλά καταδεικνύει συνοπτικά την αρχή.Στο προηγούμενο παράδειγμα, ενώ child.foo
ήταν undefined
, αναφέρθηκε parent.foo
. Μόλις ορίσαμε foo
στις child
, child.foo
είχε την τιμή 'bar'
, αλλά parent.foo
διατήρησε την αρχική του αξία. Μόλις delete child.foo
αναφέρεται και πάλι parent.foo
, που σημαίνει ότι όταν αλλάζουμε την τιμή του γονέα, child.foo
αναφέρεται στη νέα τιμή.
Ας δούμε τι ακριβώς συνέβη (για λόγους σαφέστερης απεικόνισης, θα προσποιούμαστε ότι είναι Strings
και όχι γραμματοσειρές, η διαφορά δεν έχει σημασία εδώ):
Ο τρόπος με τον οποίο λειτουργεί κάτω από την κουκούλα, και ιδιαίτερα οι ιδιαιτερότητες του new
και this
, είναι ένα θέμα για άλλη μια μέρα, αλλά η Mozilla έχει ένα αναλυτικό άρθρο σχετικά με την πρωτότυπη αλυσίδα κληρονομιάς του JavaScript αν θέλετε να διαβάσετε περισσότερα.
Το βασικό πακέτο είναι ότι τα πρωτότυπα δεν ορίζουν a type
είναι οι ίδιοι instances
και είναι μεταβλητές κατά το χρόνο εκτέλεσης, με όλα αυτά που συνεπάγονται και συνεπάγονται.
Ακόμα μαζί μου? Ας επιστρέψουμε στην ανατομή τάξεων JavaScript.
Οι ιδιότητες του πρωτοτύπου και της κατηγορίας μας δεν είναι τόσο «ενθυλακωμένες» όσο «κρέμονται επισφαλώς έξω από το παράθυρο». Πρέπει να το διορθώσουμε, αλλά πώς;
Δεν υπάρχουν παραδείγματα κώδικα εδώ. Η απάντηση είναι ότι δεν μπορείτε.
Το JavaScript δεν έχει καμία έννοια απορρήτου, αλλά έχει κλείσιμο:
function SecretiveProto() { const secret = 'The Class is a lie!' this.spillTheBeans = function() { console.log(secret) } } const blabbermouth = new SecretiveProto() try { console.log(blabbermouth.secret) } catch(e) { // TypeError: SecretiveClass.secret is not defined } blabbermouth.spillTheBeans() // 'The Class is a lie!'
Καταλαβαίνετε τι μόλις συνέβη; Εάν όχι, δεν καταλαβαίνετε το κλείσιμο. Αυτό είναι εντάξει, πραγματικά - δεν είναι τόσο εκφοβιστικά όσο έχουν φτιαχτεί, είναι εξαιρετικά χρήσιμα και πρέπει να αφιερώστε λίγο χρόνο για να μάθετε γι 'αυτά .
class
Λέξη-κλειδί;Συγγνώμη, αυτή είναι μια άλλη ερώτηση τέχνασμα. Μπορείτε να κάνετε βασικά το ίδιο πράγμα, αλλά μοιάζει με αυτό:
class SecretiveClass { constructor() { const secret = 'I am a lie!' this.spillTheBeans = function() { console.log(secret) } } looseLips() { console.log(secret) } } const liar = new SecretiveClass() try { console.log(liar.secret) } catch(e) { console.log(e) // TypeError: SecretiveClass.secret is not defined } liar.spillTheBeans() // 'I am a lie!'
Επιτρέψτε μου να ξέρω αν αυτό φαίνεται πιο εύκολο ή πιο καθαρό από ό, τι στο SecretiveProto
. Κατά την προσωπική μου άποψη, είναι κάπως χειρότερο - διακόπτει την ιδιωματική χρήση του class
δηλώσεις σε JavaScript και δεν λειτουργεί πολύ όπως θα περίμενε κανείς, ας πούμε, από την Java. Αυτό θα καταστεί σαφές από τα ακόλουθα:
SecretiveClass::looseLips()
Κάνω?Ας ανακαλύψουμε:
try { liar.looseLips() } catch(e) { // ReferenceError: secret is not defined }
Λοιπόν ... αυτό ήταν περίεργο.
ποια είναι η διαφορά μεταξύ της c corporation και της s corporation
Το μαντέψατε, αυτή είναι μια άλλη ερώτηση κόλπου - οι έμπειροι προγραμματιστές JavaScript τείνουν να αποφεύγουν και τα δύο όταν μπορούν. Εδώ είναι ένας καλός τρόπος για να κάνετε τα παραπάνω με ιδιωματική JavaScript:
function secretFactory() { const secret = 'Favor composition over inheritance, `new` is considered harmful, and the end is near!' const spillTheBeans = () => console.log(secret) return { spillTheBeans } } const leaker = secretFactory() leaker.spillTheBeans()
Αυτό δεν αφορά μόνο την αποφυγή της εγγενής ασχήμιας της κληρονομιάς ή την επιβολή ενθυλάκωσης. Σκεφτείτε τι άλλο μπορείτε να κάνετε με secretFactory
και leaker
που δεν θα μπορούσατε εύκολα να κάνετε με ένα πρωτότυπο ή μια τάξη.
Για ένα πράγμα, μπορείτε να το καταστρέψετε επειδή δεν χρειάζεται να ανησυχείτε για το πλαίσιο του this
:
const { spillTheBeans } = secretFactory() spillTheBeans() // Favor composition over inheritance, (...)
Αυτό είναι πολύ ωραίο. Εκτός από την αποφυγή new
και this
Το tomfoolery, μας επιτρέπει να χρησιμοποιήσουμε τα αντικείμενά μας εναλλάξ με τις μονάδες CommonJS και ES6. Κάνει επίσης τη σύνθεση λίγο πιο εύκολη:
function spyFactory(infiltrationTarget) { return { exfiltrate: infiltrationTarget.spillTheBeans } } const blackHat = spyFactory(leaker) blackHat.exfiltrate() // Favor composition over inheritance, (...) console.log(blackHat.infiltrationTarget) // undefined (looks like we got away with it)
Πελάτες από blackHat
δεν χρειάζεται να ανησυχείτε για το πού exfiltrate
προήλθε από, και spyFactory
δεν χρειάζεται να χαζεύεις το Function::bind
ταχυδακτυλουργία περιβάλλοντος ή έντονα ένθετες ιδιότητες. Μην ξεχνάτε, δεν χρειάζεται να ανησυχείτε πολύ για το this
σε απλό σύγχρονο διαδικαστικό κώδικα, αλλά προκαλεί όλα τα είδη προβλημάτων στον ασύγχρονο κώδικα που αποφεύγονται καλύτερα.
Με λίγη σκέψη, spyFactory
θα μπορούσε να εξελιχθεί σε ένα εξαιρετικά εξελιγμένο εργαλείο κατασκοπείας που θα μπορούσε να χειριστεί κάθε είδους στόχους διείσδυσης - ή με άλλα λόγια, ένα πρόσοψη .
Φυσικά θα μπορούσατε να το κάνετε και με μια τάξη, ή μάλλον, με μια σειρά από τάξεις, οι οποίες κληρονομούν από μια abstract class
ή interface
… εκτός από το ότι η JavaScript δεν έχει καμία έννοια περίληψης ή διεπαφών.
Ας επιστρέψουμε στο πιο φιλικό παράδειγμα για να δούμε πώς θα το εφαρμόσουμε με ένα εργοστάσιο:
function greeterFactory(greeting = 'Hello', name = 'World') { return { greet: () => `${greeting}, ${name}!` } } console.log(greeterFactory('Hey', 'folks').greet()) // Hey, folks!
Ίσως έχετε παρατηρήσει ότι αυτά τα εργοστάσια γίνονται όλο και πιο στενά καθώς προχωράμε, αλλά μην ανησυχείτε - κάνουν το ίδιο πράγμα. Οι τροχοί προπόνησης ξεκινούν, φίλοι!
Αυτό είναι ήδη λιγότερο boilerplate από το πρωτότυπο ή την έκδοση κατηγορίας του ίδιου κώδικα. Δεύτερον, επιτυγχάνει τον εγκλεισμό των ιδιοτήτων του πιο αποτελεσματικά. Επίσης, έχει χαμηλότερο αποτύπωμα μνήμης και απόδοσης σε ορισμένες περιπτώσεις (μπορεί να μην φαίνεται σαν την πρώτη ματιά, αλλά ο μεταγλωττιστής JIT εργάζεται ήσυχα πίσω από τα παρασκήνια για να μειώσει τους τύπους επανάληψης και συμπεράσματος).
Επομένως, είναι ασφαλέστερο, είναι συχνά πιο γρήγορο και είναι ευκολότερο να γράφετε κώδικα όπως αυτό. Γιατί χρειαζόμαστε ξανά μαθήματα; Ω, φυσικά, επαναχρησιμοποίηση. Τι θα συμβεί αν θέλουμε δυσαρεστημένες και ενθουσιώδεις παραλλαγές; Λοιπόν, αν χρησιμοποιούμε το ClassicalGreeting
τάξη, πιθανότατα πηδούμε απευθείας στο να ονειρευόμαστε μια ιεραρχία τάξης. Γνωρίζουμε ότι θα πρέπει να παραμετροποιήσουμε τα σημεία στίξης, οπότε θα κάνουμε λίγο επαναπροσδιορισμό και θα προσθέσουμε μερικά παιδιά:
s-corp εναντίον c-corp
// Greeting class class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World', punctuation = '!') { this.greeting = greeting this.name = name this.punctuation = punctuation } greet() { return `${this.greeting}, ${this.name}${this.punctuation}` } } // An unhappy greeting class UnhappyGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, ' :(') } } const classyUnhappyGreeting = new UnhappyGreeting('Hello', 'everyone') console.log(classyUnhappyGreeting.greet()) // Hello, everyone :( // An enthusiastic greeting class EnthusiasticGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, '!!') } greet() { return super.greet().toUpperCase() } } const greetingWithEnthusiasm = new EnthusiasticGreeting() console.log(greetingWithEnthusiasm.greet()) // HELLO, WORLD!!
Είναι μια ωραία προσέγγιση, έως ότου κάποιος έρθει και ζητήσει ένα χαρακτηριστικό που δεν ταιριάζει καθαρά στην ιεραρχία και το όλο θέμα σταματά να έχει νόημα. Βάλτε μια καρφίτσα σε αυτήν τη σκέψη ενώ προσπαθούμε να γράψουμε την ίδια λειτουργικότητα με τα εργοστάσια:
const greeterFactory = (greeting = 'Hello', name = 'World', punctuation = '!') => ({ greet: () => `${greeting}, ${name}${punctuation}` }) // Makes a greeter unhappy const unhappy = (greeter) => (greeting, name) => greeter(greeting, name, ':(') console.log(unhappy(greeterFactory)('Hello', 'everyone').greet()) // Hello, everyone :( // Makes a greeter enthusiastic const enthusiastic = (greeter) => (greeting, name) => ({ greet: () => greeter(greeting, name, '!!').greet().toUpperCase() }) console.log(enthusiastic(greeterFactory)().greet()) // HELLO, WORLD!!
Δεν είναι προφανές ότι αυτός ο κώδικας είναι καλύτερος, παρόλο που είναι λίγο πιο σύντομος. Στην πραγματικότητα, θα μπορούσατε να υποστηρίξετε ότι είναι πιο δύσκολο να διαβάσετε, και ίσως αυτή είναι μια ασαφής προσέγγιση. Δεν θα μπορούσαμε απλώς να έχουμε ένα unhappyGreeterFactory
και ένα enthusiasticGreeterFactory
;
Στη συνέχεια, ο πελάτης σας έρχεται και λέει, 'Χρειάζομαι έναν νέο χαιρετιστή που είναι δυσαρεστημένος και θέλει ολόκληρο το δωμάτιο να το γνωρίζει!'
console.log(enthusiastic(unhappy(greeterFactory))().greet()) // HELLO, WORLD :(
Εάν χρειαζόμασταν να χρησιμοποιήσουμε αυτόν τον ενθουσιώδη δυσαρεστημένο χαιρετισμό περισσότερες από μία φορές, θα μπορούσαμε να διευκολύνουμε τον εαυτό μας:
const aggressiveGreeterFactory = enthusiastic(unhappy(greeterFactory)) console.log(aggressiveGreeterFactory('You're late', 'Jim').greet())
Υπάρχουν προσεγγίσεις σε αυτό το στυλ σύνθεσης που λειτουργούν με πρωτότυπα ή τάξεις. Για παράδειγμα, θα μπορούσατε να ξανασκεφτείτε το UnhappyGreeting
και EnthusiasticGreeting
όπως και διακοσμητές . Θα χρειαζόταν ακόμη περισσότερη πλάκα από την προσέγγιση λειτουργικού στιλ που χρησιμοποιήθηκε παραπάνω, αλλά αυτή είναι η τιμή που πληρώνετε για την ασφάλεια και τον εγκλεισμό πραγματικός τάξεις.
Το θέμα είναι ότι, σε JavaScript, δεν λαμβάνετε αυτήν την αυτόματη ασφάλεια. Πλαίσια JavaScript που δίνουν έμφαση class
Η χρήση κάνει πολλή «μαγεία» στο χαρτί πάνω σε αυτά τα είδη προβλημάτων και αναγκάζει τις τάξεις να συμπεριφέρονται. Ρίξτε μια ματιά στο Polymer's ElementMixin
πηγαίος κώδικας κάποια στιγμή, τολμώ. Είναι τα επίπεδα του arcana JavaScript, και εννοώ ότι χωρίς ειρωνεία ή σαρκασμό.
Φυσικά, μπορούμε να διορθώσουμε ορισμένα από τα θέματα που συζητήθηκαν παραπάνω με Object.freeze
ή Object.defineProperties
σε μεγαλύτερο ή μικρότερο αποτέλεσμα. Γιατί λοιπόν να μιμηθείτε τη φόρμα χωρίς τη συνάρτηση, αγνοώντας τα εργαλεία JavaScript κάνει να μας παράσχετε εγγενώς ότι ενδέχεται να μην βρίσκουμε σε γλώσσες όπως η Java; Θα χρησιμοποιούσατε ένα σφυρί με την ένδειξη 'κατσαβίδι' για να οδηγήσετε μια βίδα, όταν η εργαλειοθήκη σας είχε ως πραγματικό κατσαβίδι ακριβώς δίπλα του;
Οι προγραμματιστές JavaScript υπογραμμίζουν συχνά τα καλά μέρη της γλώσσας, τόσο συνομιλία όσο και σε σχέση με το το βιβλίο με το ίδιο όνομα . Προσπαθούμε να αποφύγουμε τις παγίδες που τίθενται από τις πιο αμφισβητήσιμες επιλογές σχεδιασμού γλώσσας και εμμένουμε στα μέρη που μας επιτρέπουν να γράφουμε καθαρό, αναγνώσιμο, ελαχιστοποίηση σφαλμάτων, επαναχρησιμοποιήσιμο κώδικα.
Υπάρχουν εύλογα επιχειρήματα σχετικά με τα τμήματα της JavaScript που πληρούν τις προϋποθέσεις, αλλά ελπίζω να σας πείσω ότι class
δεν είναι ένα από αυτά. Σε αντίθετη περίπτωση, ελπίζουμε ότι καταλαβαίνετε ότι η κληρονομιά στο JavaScript μπορεί να είναι ένα μπερδεμένο χάος και ότι class
ούτε το επιδιορθώνει ούτε σας βοηθά να κατανοήσετε τα πρωτότυπα. Πρόσθετη πίστωση εάν έχετε λάβει υπόψη τις υποδείξεις ότι τα αντικειμενικά σχέδια σχεδίασης λειτουργούν καλά χωρίς μαθήματα ή κληρονομιά ES6.
Δεν σας λέω να αποφύγετε class
εξ ολοκλήρου. Μερικές φορές χρειάζεστε κληρονομιά και class
παρέχει καθαρότερη σύνταξη για αυτό. Συγκεκριμένα, class X extends Y
είναι πολύ καλύτερο από την παλιά πρωτότυπη προσέγγιση. Εκτός αυτού, πολλά δημοφιλή front-end πλαίσια ενθαρρύνουν τη χρήση του και μάλλον θα πρέπει να αποφύγετε να γράφετε περίεργο μη τυποποιημένο κώδικα μόνο κατ 'αρχήν. Δεν μου αρέσει που πηγαίνει.
Στους εφιάλτες μου, μια ολόκληρη γενιά βιβλιοθηκών JavaScript γράφεται χρησιμοποιώντας class
, με την προσδοκία ότι θα συμπεριφέρεται παρόμοια με άλλες δημοφιλείς γλώσσες. Ανακαλύπτονται ολόκληρες νέες κατηγορίες σφαλμάτων (pun προορίζονται). Οι παλιοί αναστηθούν που θα μπορούσαν εύκολα να είχαν μείνει στο Graveyard of Malformed JavaScript αν δεν είχαμε απρόσεκτα πέσει στο class
παγίδα. Οι έμπειροι προγραμματιστές JavaScript μαστίζονται από αυτά τα τέρατα, γιατί αυτό που είναι δημοφιλές δεν είναι πάντα καλό.
Τελικά όλοι παραιτούμε από την απογοήτευση και αρχίζουμε να ανακαλύπτουμε ξανά τους τροχούς στο Rust, Go, Haskell ή ποιος ξέρει τι άλλο και στη συνέχεια να μεταγλωττίζουμε στο Wasm για το διαδίκτυο και νέα πλαίσια ιστού και βιβλιοθήκες πολλαπλασιάζονται σε πολύγλωσσο άπειρο.
Με κρατάει πραγματικά τη νύχτα.
Το ES6 είναι η τελευταία σταθερή εφαρμογή του ECMAScript, το ανοιχτό πρότυπο στο οποίο βασίζεται η JavaScript. Προσθέτει μια σειρά νέων δυνατοτήτων στη γλώσσα, όπως ένα επίσημο σύστημα λειτουργικών μονάδων, μεταβλητές και σταθερές με αποκλεισμούς, λειτουργίες βέλους και πολλές άλλες νέες λέξεις-κλειδιά, σύνταξη και ενσωματωμένα αντικείμενα.
Το ES6 (ES2015) είναι το πιο πρόσφατο πρότυπο που είναι σταθερό και πλήρως υλοποιημένο (εκτός από σωστές ουρές κλήσεις και κάποιες αποχρώσεις) στις τελευταίες εκδόσεις μεγάλων προγραμμάτων περιήγησης και άλλων περιβάλλοντων JS. Οι ES7 (ES2016) και ES8 (ES2017) είναι επίσης σταθερές προδιαγραφές, αλλά η εφαρμογή είναι αρκετά μικτή.
Το JavaScript έχει ισχυρή υποστήριξη για αντικειμενοστρεφή προγραμματισμό, αλλά χρησιμοποιεί ένα διαφορετικό μοντέλο κληρονομιάς (πρωτότυπο) σε σύγκριση με τις πιο δημοφιλείς γλώσσες OO (που χρησιμοποιούν κλασική κληρονομιά). Υποστηρίζει επίσης διαδικαστικά και λειτουργικά στυλ προγραμματισμού.
Στο ES6, η λέξη-κλειδί «class» και οι σχετικές λειτουργίες είναι μια νέα προσέγγιση για τη δημιουργία πρωτότυπων κατασκευαστών. Δεν είναι αληθινά μαθήματα με τρόπο που θα ήταν εξοικειωμένοι με τους χρήστες των περισσότερων άλλων αντικειμενοστρεφών γλωσσών.
Κάποιος μπορεί να εφαρμόσει κληρονομιά στο JavaScript ES6 μέσω των λέξεων-κλειδιών «class» και «extends». Μια άλλη προσέγγιση είναι μέσω του ιδιώματος συνάρτησης «κατασκευαστής» συν η εκχώρηση συναρτήσεων και στατικών ιδιοτήτων στο πρωτότυπο του κατασκευαστή.
Στην πρωτότυπη κληρονομιά, τα πρωτότυπα είναι περιπτώσεις αντικειμένων στις οποίες οι θυγατρικές παρουσίες εκχωρούν απροσδιόριστες ιδιότητες. Αντίθετα, οι τάξεις στην κλασική κληρονομιά είναι ορισμοί τύπων, από τους οποίους οι παιδικές τάξεις κληρονομούν μεθόδους και ιδιότητες κατά την περίοδο.