Hacks για τη δημιουργία συστοιχιών JavaScript

Διορατικές συμβουλές για τη δημιουργία και την κλωνοποίηση συστοιχιών σε JavaScript.

Μια πολύ σημαντική πτυχή κάθε γλώσσας προγραμματισμού είναι οι τύποι δεδομένων και οι δομές που διατίθενται στη γλώσσα. Οι περισσότερες γλώσσες προγραμματισμού παρέχουν τύπους δεδομένων για την αναπαράσταση και την εργασία με πολύπλοκα δεδομένα. Εάν έχετε εργαστεί με γλώσσες όπως Python ή Ruby, θα πρέπει να έχετε δει τύπους δεδομένων όπως λίστες , σύνολα , πλειάδες , κατακερματισμούς , υπαγόρευση και ούτω καθεξής.

Στο JavaScript, δεν υπάρχουν τόσο πολλοί πολύπλοκοι τύποι δεδομένων - έχετε απλά πίνακες και αντικείμενα . Ωστόσο, στο ES6, δύο τύποι δεδομένων και δομές προστέθηκαν στη γλώσσα, όπως σύμβολα , σύνολα και χάρτες .

Οι πίνακες στη JavaScript είναι αντικείμενα τύπου λίστας υψηλού επιπέδου με ιδιότητα μήκους και ακέραιες ιδιότητες ως ευρετήρια.

Σε αυτό το άρθρο, μοιράζομαι μερικά hacks για τη δημιουργία νέων συστοιχιών JavaScript ή για την κλωνοποίηση ήδη υπαρχόντων.

Δημιουργία συστοιχιών: Ο κατασκευαστής συστοιχιών

Η πιο δημοφιλής μέθοδος για τη δημιουργία συστοιχιών είναι η χρήση της γραμματικής σύνταξης του πίνακα , η οποία είναι πολύ απλή. Ωστόσο, όταν θέλετε να δημιουργήσετε δυναμικούς πίνακες, η κυριολεκτική σύνταξη του πίνακα δεν μπορεί πάντα να είναι η καλύτερη μέθοδος. Μια εναλλακτική μέθοδος είναι η χρήση του Arrayκατασκευαστή.

Ακολουθεί ένα απλό απόσπασμα κώδικα που δείχνει τη χρήση του Arrayκατασκευαστή.

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

Νέες συστοιχίες: Με καθορισμένο μήκος

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

Από το παραπάνω απόσπασμα, μπορεί να μπείτε στον πειρασμό να σκεφτείτε ότι κάθε κλειδί στον πίνακα ορίστηκε σε τιμή undefined. Αλλά η πραγματικότητα είναι ότι αυτά τα κλειδιά δεν είχαν τεθεί ποτέ (δεν υπάρχουν).

Η παρακάτω εικόνα το καθιστά σαφέστερο:

Αυτό το καθιστά άχρηστο να προσπαθήσουμε να χρησιμοποιήσουμε οποιαδήποτε από τις μεθόδους επανάληψης του πίνακα όπως map(), filter()ή reduce()να χειριστούμε τον πίνακα. Ας πούμε ότι θέλουμε να συμπληρώσουμε κάθε ευρετήριο στον πίνακα με τον αριθμό 5ως τιμή. Θα επιχειρήσουμε τα εξής:

Μπορούμε να δούμε ότι map()δεν λειτούργησε εδώ, επειδή οι ιδιότητες ευρετηρίου δεν υπάρχουν στον πίνακα - υπάρχει μόνο η lengthιδιότητα.

Ας δούμε διαφορετικούς τρόπους για να διορθώσουμε αυτό το ζήτημα.

1. Χρήση του Array.prototype.fill ()

Η fill()μέθοδος γεμίζει όλα τα στοιχεία ενός πίνακα από ένα ευρετήριο έναρξης έως ένα ευρετήριο τέλους με μια στατική τιμή. Ο τελικός δείκτης δεν περιλαμβάνεται. Μπορείτε να μάθετε περισσότερα για fill()εδώ.

Σημειώστε ότι fill()θα λειτουργεί μόνο σε προγράμματα περιήγησης με υποστήριξη ES6.

Εδώ είναι μια απλή απεικόνιση:

Εδώ, καταφέραμε να συμπληρώσουμε όλα τα στοιχεία του πίνακα που δημιουργήσαμε 5. Μπορείτε να ορίσετε οποιαδήποτε στατική τιμή για διαφορετικά ευρετήρια του πίνακα χρησιμοποιώντας τη fill()μέθοδο.

2. Χρήση του Array.from ()

Η Array.from()μέθοδος δημιουργεί μια νέα, ρηχή αντιγραφή Arrayαπό ένα αντικείμενο που μοιάζει με πίνακα ή επαναλαμβανόμενο. Μπορείτε να μάθετε περισσότερα για Array.from()εδώ.

Σημειώστε ότι Array.from()θα λειτουργεί μόνο σε προγράμματα περιήγησης με υποστήριξη ES6.

Εδώ είναι μια απλή απεικόνιση:

Εδώ, έχουμε πλέον undefinedκαθορισμένες τιμές για κάθε στοιχείο του πίνακα Array.from(). Αυτό σημαίνει ότι μπορούμε τώρα να προχωρήσουμε και να χρησιμοποιήσουμε μεθόδους όπως .map()και .filter()στον πίνακα, καθώς οι ιδιότητες ευρετηρίου υπάρχουν τώρα.

Ένα ακόμη πράγμα που αξίζει να σημειωθεί Array.from()είναι ότι μπορεί να πάρει ένα δεύτερο επιχείρημα, το οποίο είναι μια λειτουργία χάρτη. Θα καλείται σε κάθε στοιχείο του πίνακα. Αυτό το καθιστά περιττό να καλείτε .map()μετά Array.from().

Εδώ είναι ένα απλό παράδειγμα:

3. Χρήση του Spread Operator

Ο χειριστής spread( ...), που προστέθηκε στο ES6, μπορεί να χρησιμοποιηθεί για τη διάδοση των στοιχείων του πίνακα, θέτοντας τα στοιχεία που λείπουν σε τιμή undefined. Αυτό θα παράγει το ίδιο αποτέλεσμα με την απλή κλήση Array.from()με τον πίνακα ως το μόνο επιχείρημα.

Ακολουθεί μια απλή απεικόνιση της χρήσης του τελεστή spread:

Μπορείτε να προχωρήσετε και να χρησιμοποιήσετε μεθόδους όπως .map()και .filter()στον πίνακα, καθώς οι ιδιότητες ευρετηρίου υπάρχουν τώρα.

Χρησιμοποιώντας το Array.of ()

Όπως είδαμε με τη δημιουργία νέων συστοιχιών χρησιμοποιώντας τον Arrayκατασκευαστή ή τη λειτουργία, Array.of()συμπεριφέρεται με παρόμοιο τρόπο. Στην πραγματικότητα, η μόνη διαφορά μεταξύ Array.of()και Arrayείναι στον τρόπο με τον οποίο χειρίζονται ένα ενιαίο ακέραιο όρισμα που τους διαβιβάζεται.

Ενώ Array.of(5)δημιουργεί έναν νέο πίνακα με ένα μόνο στοιχείο, 5και μια ιδιότητα μήκους 1, Array(5)δημιουργεί έναν νέο κενό πίνακα με 5 κενές υποδοχές και μια ιδιότητα μήκους 5.

var array1 = Array.of(5); // [5] var array2 = Array(5); // Array(5) {length: 5}

Εκτός από αυτήν τη μεγάλη διαφορά, Array.of()συμπεριφέρεται ακριβώς όπως ο Arrayκατασκευαστής. Μπορείτε να μάθετε περισσότερα για Array.of()εδώ.

Σημειώστε ότι Array.of()θα λειτουργεί μόνο σε προγράμματα περιήγησης με υποστήριξη ES6.

Μετατροπή σε συστοιχίες: Συγκεντρώσεις και επαναλήψεις

Εάν έχετε γράψει συναρτήσεις JavaScript αρκετά καιρό, θα πρέπει να γνωρίζετε ήδη για το argumentsαντικείμενο - το οποίο είναι ένα αντικείμενο που μοιάζει με πίνακα διαθέσιμο σε κάθε συνάρτηση για να διατηρεί τη λίστα με τα επιχειρήματα που έλαβε η συνάρτηση. Αν και το argumentsαντικείμενο μοιάζει με πίνακα, δεν έχει πρόσβαση στις Array.prototypeμεθόδους.

Πριν από το ES6, συνήθως βλέπετε ένα απόσπασμα κώδικα όπως το ακόλουθο όταν προσπαθείτε να μετατρέψετε το argumentsαντικείμενο σε πίνακα

Με Array.from()ή τον τελεστή επέκτασης, μπορείτε εύκολα να μετατρέψετε οποιοδήποτε αντικείμενο που μοιάζει με πίνακα σε πίνακα. Ως εκ τούτου, αντί να το κάνετε αυτό:

var args = Array.prototype.slice.call(arguments);

μπορείτε να κάνετε ένα από αυτά:

// Using Array.from() var args = Array.from(arguments); // Using the Spread operator var args = [...arguments];

Αυτά ισχύουν επίσης για επαναλήψεις όπως φαίνεται στην παρακάτω εικόνα:

Μελέτη περίπτωσης: Λειτουργία εύρους

Ως μελέτη περίπτωσης προτού προχωρήσουμε, θα δημιουργήσουμε μια απλή range()λειτουργία για την εφαρμογή του νέου hack hack που μόλις μάθαμε. Η συνάρτηση έχει την ακόλουθη υπογραφή:

range(start: number, end: number, step: number) => Array

Εδώ είναι το απόσπασμα κώδικα:

Σε αυτό το απόσπασμα κώδικα, χρησιμοποιούσαμε Array.from()τη νέα σειρά εύρους δυναμικού μήκους και στη συνέχεια συμπληρώσαμε τους διαδοχικά αυξημένους αριθμούς παρέχοντας μια λειτουργία χαρτογράφησης.

Λάβετε υπόψη ότι το παραπάνω απόσπασμα κώδικα δεν θα λειτουργεί για προγράμματα περιήγησης χωρίς υποστήριξη ES6, εκτός εάν χρησιμοποιείτε polyfills.

Ακολουθούν ορισμένα αποτελέσματα από την κλήση της range()συνάρτησης που ορίζεται στο παραπάνω απόσπασμα κώδικα:

Μπορείτε να λάβετε μια ζωντανή επίδειξη κώδικα εκτελώντας την ακόλουθη πένα στο Codepen :

Cloning Arrays: Η πρόκληση

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

Οι πίνακες, όπως και κάθε άλλο αντικείμενο στη JavaScript, είναι τύποι αναφοράς. Αυτό σημαίνει ότι οι πίνακες αντιγράφονται με αναφορά και όχι από τιμή.

Η αποθήκευση τύπων αναφοράς με αυτόν τον τρόπο έχει τις ακόλουθες συνέπειες:

1. Παρόμοιες σειρές δεν είναι ίδιες.

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

2. Οι πίνακες αντιγράφονται με αναφορά και όχι από την τιμή.

Εδώ, προσπαθούμε να αντιγράψετε array1σε array2, αλλά αυτό που ουσιαστικά κάνουν είναι στραμμένη array2προς την ίδια θέση στη μνήμη ότι array1τα σημεία για να. Ως εκ τούτου, array1και οι δύο και array2δείξτε την ίδια θέση στη μνήμη και είναι ίσες.

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

Συστοιχίες κλωνοποίησης: The Hacks

1. Χρήση του Array.prototype.slice ()

Η slice()μέθοδος δημιουργεί ένα ρηχό αντίγραφο ενός τμήματος ενός πίνακα χωρίς τροποποίηση του πίνακα. Μπορείτε να μάθετε περισσότερα για slice()εδώ.

Το κόλπο είναι να καλέσετε slice()είτε με 0το μόνο επιχείρημα είτε χωρίς καθόλου επιχειρήματα:

// with O as only argument array.slice(0); // without argument array.slice();

Εδώ είναι μια απλή απεικόνιση της κλωνοποίησης ενός πίνακα με slice():

Εδώ, μπορείτε να δείτε ότι array2είναι ένας κλώνος array1με τα ίδια αντικείμενα και μήκος. Ωστόσο, δείχνουν διαφορετικές τοποθεσίες στη μνήμη, και ως εκ τούτου δεν είναι ίσες. Παρατηρείτε επίσης ότι όταν κάνουμε μια αλλαγή array2με την κατάργηση του τελευταίου στοιχείου, array1παραμένει αμετάβλητο.

2. Χρήση του Array.prototype.concat ()

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

Το κόλπο είναι να καλέσετε concat()είτε με έναν κενό πίνακα ( []) ως όρισμα είτε χωρίς ορίσματα:

// with an empty array array.concat([]); // without argument array.concat();

Η κλωνοποίηση ενός πίνακα με concat()είναι παρόμοια με τη χρήση slice(). Εδώ είναι μια απλή απεικόνιση της κλωνοποίησης ενός πίνακα με concat():

3. Χρήση του Array.from ()

Όπως είδαμε νωρίτερα, Array.from()μπορεί να χρησιμοποιηθεί για τη δημιουργία ενός νέου πίνακα που είναι ένα ρηχό αντίγραφο του αρχικού πίνακα. Εδώ είναι μια απλή απεικόνιση:

4. Χρήση του Array Destructuring

Με το ES6, έχουμε μερικά πιο ισχυρά εργαλεία στην εργαλειοθήκη μας, όπως η καταστροφή , η εξάπλωσηχειριστής , λειτουργίες βέλους και ούτω καθεξής. Το Destructuring είναι ένα πολύ ισχυρό εργαλείο για την εξαγωγή δεδομένων από σύνθετους τύπους όπως πίνακες και αντικείμενα.

Το κόλπο είναι να χρησιμοποιήσετε μια τεχνική που ονομάζεται παραμέτρους ανάπαυσης, η οποία περιλαμβάνει έναν συνδυασμό καταστροφής συστοιχιών και του τελεστή διασποράς, όπως φαίνεται στο παρακάτω απόσπασμα:

let [...arrayClone] = originalArray;

Το παραπάνω απόσπασμα δημιουργεί μια μεταβλητή που ονομάζεται arrayCloneπου είναι κλώνος του originalArray. Εδώ είναι μια απλή απεικόνιση της κλωνοποίησης ενός πίνακα χρησιμοποιώντας την καταστροφή πίνακα:

Κλωνοποίηση: Ρηχό έναντι Βαθύ

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

Εδώ είναι μια πολύ απλή απόδειξη αυτού:

Παρατηρήστε ότι η τροποποίηση του ένθετου πίνακα array1τροποποίησε επίσης τον ένθετο πίνακα array2και αντίστροφα.

Η λύση σε αυτό το πρόβλημα είναι να δημιουργήσετε ένα βαθύ αντίγραφο του πίνακα και υπάρχουν μερικοί τρόποι για να το κάνετε αυτό.

1. Η τεχνική JSON

Ο ευκολότερος τρόπος για να δημιουργήσετε ένα βαθύ αντίγραφο ενός πίνακα είναι χρησιμοποιώντας έναν συνδυασμό JSON.stringify()και JSON.parse().

JSON.stringify()μετατρέπει μια τιμή JavaScript σε μια έγκυρη συμβολοσειρά JSON, ενώ JSON.parse()μετατρέπει μια συμβολοσειρά JSON σε μια αντίστοιχη τιμή ή αντικείμενο JavaScript.

Εδώ είναι ένα απλό παράδειγμα:

Η τεχνική JSON έχει κάποια ελαττώματα ειδικά όταν εμπλέκονται τιμές εκτός από χορδές, αριθμούς και booleans.

Αυτά τα ελαττώματα στην τεχνική JSON μπορούν να αποδοθούν σε μεγάλο βαθμό στον τρόπο με τον οποίο η JSON.stringify()μέθοδος μετατρέπει τιμές σε συμβολοσειρά JSON.

Εδώ είναι μια απλή επίδειξη αυτού του ελαττώματος στην προσπάθεια για JSON.stringify()μια τιμή που περιέχει ένθετη συνάρτηση.

2. Βοηθός αντιγράφων σε βάθος

Μια βιώσιμη εναλλακτική λύση για την τεχνική JSON θα είναι να εφαρμόσετε τη δική σας λειτουργία βοηθητικού αντιγράφου για κλωνοποίηση τύπων αναφοράς, είτε πρόκειται για πίνακες είτε για αντικείμενα.

Εδώ είναι μια πολύ απλή και μινιμαλιστική λειτουργία βαθιάς αντιγραφής που ονομάζεται deepClone:

Τώρα αυτή δεν είναι η καλύτερη λειτουργία deep copy εκεί έξω, όπως θα δείτε σύντομα με μερικές βιβλιοθήκες JavaScript - ωστόσο, εκτελεί αντιγραφή σε βάθος σε αρκετά καλό βαθμό.

3. Χρήση βιβλιοθηκών JavaScript

Η λειτουργία βοηθητικού αντιγράφου σε βάθος που μόλις ορίσαμε δεν είναι αρκετά ισχυρή για την κλωνοποίηση όλων των ειδών δεδομένων JavaScript που ενδέχεται να είναι ένθετα σε σύνθετα αντικείμενα ή πίνακες.

Οι βιβλιοθήκες JavaScript όπως το Lodash και το jQuery παρέχουν πιο ισχυρές λειτουργίες βάθους αντιγραφής με υποστήριξη για διαφορετικά είδη δεδομένων JavaScript.

Εδώ είναι ένα παράδειγμα που χρησιμοποιεί _.cloneDeep()από τη βιβλιοθήκη του Lodash:

Εδώ είναι το ίδιο παράδειγμα, αλλά χρησιμοποιώντας $.extend()από τη βιβλιοθήκη jQuery:

συμπέρασμα

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

Έχουμε επίσης δει πώς μερικά από τα νέα χαρακτηριστικά και βελτιώσεις που εισάγονται στο ES6 μπορούν να μας επιτρέψουν να εκτελέσουμε αποτελεσματικά συγκεκριμένους χειρισμούς σε συστοιχίες.

Χρησιμοποιήσαμε λειτουργίες όπως η καταστροφή και ο τελεστής διασποράς για κλωνοποίηση και διάδοση συστοιχιών. Μπορείτε να μάθετε περισσότερα σχετικά με την καταστροφή από αυτό το άρθρο.

Χτυπήστε και ακολουθήστε

Εάν βρήκατε αυτό το άρθρο διορατικό, μπορείτε να δώσετε χειροκροτήματα αν δεν σας πειράζει.

Μπορείτε επίσης να με ακολουθήσετε στο Medium (Glad Chinda) για πιο διορατικά άρθρα που μπορεί να σας φανούν χρήσιμα. Μπορείτε επίσης να με ακολουθήσετε στο Twitter (@gladchinda).

Χαρούμενο χάκερ…