Γνωρίζουμε πολλούς τελεστές από το σχολείο. Είναι πράγματα όπως η πρόσθεση +, ο πολλαπλασιασμός *, η αφαίρεση - και ούτω καθεξής.
Σε αυτό το κεφάλαιο, θα ξεκινήσουμε με απλούς τελεστές και, στη συνέχεια, θα επικεντρωθούμε σε συγκεκριμένες πτυχές της JavaScript, που δεν καλύπτονται από στη σχολική αριθμητική.
Πριν προχωρήσουμε, ας κατανοήσουμε κάποια κοινή ορολογία.
-
Ένας τελεστής -- είναι για το οποίο εφαρμόζονται οι τελεστές. Για παράδειγμα, στον πολλαπλασιασμό του
5 * 2υπάρχουν δύο τελεστές: ο αριστερός τελεστής είναι5και ο δεξιός τελεστής είναι2. Μερικές φορές, οι άνθρωποι αποκαλούν αυτά "arguments" αντί για "τελεστές. -
Ένας τελεστής είναι unary εάν έχει έναν μόνο τελεστή. Για παράδειγμα, η unary άρνηση
-αντιστρέφει το σύμβολο ενός αριθμού:let x = 1; *!* x = -x; */!* alert( x ); // -1, Εφαρμόστηκε unary άρνηση
-
Ένας τελεστής είναι δυαδικός εάν έχει δύο τελεστές. Το ίδιο μείον υπάρχει και σε δυαδική μορφή:
let x = 1, y = 3; alert( y - x ); // 2, δυαδικό μείον αφαιρεί τις τιμές
Επισήμως, στα παραπάνω παραδείγματα έχουμε δύο διαφορετικούς τελεστές που μοιράζονται το ίδιο σύμβολο: τον τελεστή άρνησης, έναν unary τελεστή που αντιστρέφει το σύμβολο και τον τελεστή αφαίρεσης, έναν δυαδικό τελεστή που αφαιρεί έναν αριθμό από τον άλλο.
Υποστηρίζονται οι ακόλουθες μαθηματικές λειτουργίες:
- Πρόσθεση
+, - Αφαίρεση
-, - Πολλαπλασιασμός
*, - Διαίρεση
/, - Υπόλοιπο
%, - Εκθετικό
**.
Τα πρώτα τέσσερα είναι απλά, ενώ τα % και ** χρειάζονται λίγα λόγια για αυτά.
Ο υπόλοιπος τελεστής %, παρά την εμφάνισή του, δεν σχετίζεται με τα ποσοστά.
Το αποτέλεσμα του a % b είναι το υπόλοιπο της ακέραιας διαίρεσης του a από το b.
Για παράδειγμα:
<<<<<<< HEAD
alert( 5 % 2 ); // 1, ένα υπόλοιπο 5 διαιρούμενο με 2
alert( 8 % 3 ); // 2, ένα υπόλοιπο 8 διαιρούμενο με 3
alert( 8 % 4 ); // 3, ένα υπόλοιπο 8 διαιρούμενο με 4
=======
alert( 5 % 2 ); // 1, the remainder of 5 divided by 2
alert( 8 % 3 ); // 2, the remainder of 8 divided by 3
alert( 8 % 4 ); // 0, the remainder of 8 divided by 4
>>>>>>> 1ce5644a15ee141fbe78c0fb79c8f40d870d7043Ο εκθετικός τελεστής a ** b πολλαπλασιάζει το a με τον εαυτό του b φορές.
Για παράδειγμα:
alert( 2 ** 2 ); // 4 (2 πολλαπλασιάζεται με τον εαυτό του 2 φορές)
alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 φορές)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 φορές)Μαθηματικά, ο εκθετικός ορίζεται και για μη ακέραιους αριθμούς. Για παράδειγμα, μια τετραγωνική ρίζα εκτείνεται με το 1/2:
alert( 4 ** (1/2) ); // 2 (η ισχύς του 1/2 είναι η ίδια με μια τετραγωνική ρίζα)
alert( 8 ** (1/3) ); // 2 (η ισχύς του 1/3 είναι η ίδια με μια τετραγωνική ρίζα)Ας συναντήσουμε τις δυνατότητες των τελεστών JavaScript που είναι πέρα από τη σχολική αριθμητική.
Συνήθως, ο τελεστής συν + αθροίζει τους αριθμούς
Όμως, εάν το δυαδικό + εφαρμόζεται σε συμβολοσειρές, τα συγχωνεύει:
let s = "my" + "string";
alert(s); // mystringΣημειώστε ότι εάν κάποιος από τους τελεστές είναι συμβολοσειρά, τότε και ο άλλος μετατρέπεται σε συμβολοσειρά.
Για παράδειγμα:
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"Βλέπετε, δεν έχει σημασία αν ο πρώτος τελεστής είναι μια συμβολοσειρά ή ο δεύτερος.
Εδώ είναι ένα πιο περίπλοκο παράδειγμα:
alert(2 + 2 + '1' ); // "41" and not "221"Εδώ, οι τελεστές δουλεύουν το ένα μετά το άλλο. Το πρώτο + αθροίζει δύο αριθμούς, οπότε επιστρέφει 4, και στη συνέχεια το επόμενο + προσθέτει τη συμβολοσειρά 1 σε αυτό, έτσι είναι σαν 4 + '1' = 41.
Το δυαδικό + είναι ο μόνος τελεστής που υποστηρίζει συμβολοσειρές τόσο μακριά. Άλλοι αριθμητικοί τελεστές λειτουργούν μόνο με αριθμούς και μετατρέπουν πάντα τους τελεστές τους σε αριθμούς.
Εδώ είναι το παράδειγμα για αφαίρεση και διαίρεση:
alert( 6 - '2' ); // 4, μετατρέπει το `2` σε έναν αριθμό
alert( '6' / '2' ); // 3, μετατρέπει και τους δύο τελεστές σε αριθμούςΤο συν + υπάρχει σε δύο μορφές: τη δυαδική μορφή που χρησιμοποιήσαμε παραπάνω και την unary μορφή.
Το unary συν ή, με άλλα λόγια, ο τελεστής συν + που εφαρμόζεται σε μία μόνο τιμή, δεν κάνει τίποτα στους αριθμούς. Αλλά αν ο τελεστής δεν είναι αριθμός, το unary συν το μετατρέπει σε αριθμό.
Για παράδειγμα:
// Δεν επηρεάζει τους αριθμούς
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
*!*
// Μετατρέπει σε μη αριθμούς
alert( +true ); // 1
alert( +"" ); // 0
*/!*Κάνει το ίδιο πράγμα με το Number (...), αλλά είναι μικρότερο.
Η ανάγκη μετατροπής συμβολοσειρών σε αριθμούς προκύπτει πολύ συχνά. Για παράδειγμα, εάν λαμβάνουμε τιμές από πεδία φόρμας HTML, συνήθως είναι συμβολοσειρές. Τι γίνεται αν θέλουμε να τα αθροίσουμε;
Το δυαδικό συν θα τους προσθέσει ως συμβολοσειρές:
let apples = "2";
let oranges = "3";
alert( apples + oranges ); // "23", το δυα συν τα συνδεεί σειριακά τις συμβολοσειρέςΕάν θέλουμε να τους αντιμετωπίσουμε ως αριθμούς, πρέπει να τα μετατρέψουμε και στη συνέχεια να τους αθροίσουμε:
let apples = "2";
let oranges = "3";
*!*
// Και οι δύο τιμές μετατράπηκαν σε αριθμούς πριν από το δυαδικό συν
alert( +apples + +oranges ); // 5
*/!*
// η μεγαλύτερη παραλλαγή
// alert( Number(apples) + Number(oranges) ); // 5Από την άποψη ενός μαθηματικού, η αφθονία των συν μπορεί να φαίνεται περίεργη. Αλλά από τη μερία ενός προγραμματιστή, δεν υπάρχει τίποτα το ιδιαίτερο: εφαρμόζονται πρώτα τα unary plus, μετατρέπουν τις συμβολοσειρές σε αριθμούς και, στη συνέχεια, το δυαδικό συν τους αθροίζει.
Γιατί εφαρμόζονται unary plus σε τιμές πριν από τις δυαδικές; Όπως θα δούμε, αυτό οφείλεται στην υψηλότερη προτεραιότητά τους.
Εάν μια παράσταση έχει περισσότερους από έναν τελεστές, η εντολή εκτέλεσης καθορίζεται από την προτεραιότητα ή, με άλλα λόγια, την προεπιλεγμένη σειρά προτεραιότητας των τελεστών.
Από το σχολείο, όλοι γνωρίζουμε ότι ο πολλαπλασιασμός εκφράσεται 1 + 2 * 2 πρέπει να υπολογίζεται πριν από την πρόσθεση. Αυτό είναι ακριβώς το θέμα προτεραιότητας. Ο πολλαπλασιασμός λέγεται ότι έχει μεγαλύτερη προτεραιότητα από την προσθήκη.
Οι παρενθέσεις παρακάμπτουν κάθε προτεραιότητα, οπότε αν δεν είμαστε ικανοποιημένοι με την προεπιλεγμένη σειρά, μπορούμε να τις χρησιμοποιήσουμε και να της αλλάξουμε. Για παράδειγμα, γράψτε (1 + 2) * 2.
Υπάρχουν πολλοί τελεστές σε JavaScript. Κάθε τελεστής έχει έναν αντίστοιχο αριθμό προτεραιότητας. Εκείνος με τον μεγαλύτερο αριθμό εκτελείται πρώτα. Εάν η προτεραιότητα είναι η ίδια, η εντολή εκτέλεσης είναι από αριστερά προς τα δεξιά.
Ακολουθεί ένα απόσπασμα από τον πίνακα προτεραιότητας(δεν χρειάζεται να το θυμάστε αυτό, αλλά σημειώστε τα unary τελεστές είναι υψηλότεροι από τους αντίστοιχους δυαδικούς):
| Precedence | Name | Sign |
|---|---|---|
| ... | ... | ... |
| 14 | unary plus | + |
| 14 | unary negation | - |
| 13 | exponentiation | ** |
| 12 | multiplication | * |
| 12 | division | / |
| 11 | addition | + |
| 11 | subtraction | - |
| ... | ... | ... |
| 2 | assignment | = |
| ... | ... | ... |
Όπως μπορούμε να δούμε, το "unary plus" έχει προτεραιότητα 17 που είναι υψηλότερη από το 13 της "προσθήκης" (δυαδικό συν). Γι 'αυτό, στην έκφραση "+apples + +oranges", τα unary plus λειτουργούν πριν από την πρόσθεση.
Ας σημειώσουμε ότι μια ανάθεση = είναι επίσης τελεστής. Παρατίθεται στον πίνακα προτεραιότητας με την πολύ χαμηλή προτεραιότητα του 3.
Γι 'αυτό, όταν εκχωρούμε μια μεταβλητή, όπως x = 2 * 2 + 1, οι υπολογισμοί γίνονται πρώτα και στη συνέχεια αξιολογείται το =, αποθηκεύοντας το αποτέλεσμα στο x.
let x = 2 * 2 + 1;
alert( x ); // 5Το γεγονός ότι το = είναι τελεστής και όχι ένα "μαγικό" γλωσσικό κατασκεύασμα έχει ενδιαφέρουσα επίπτωση.
Οι περισσότεροι τελεστές στην JavaScript επιστρέφουν μια τιμή. Αυτό είναι προφανές για τα + και -, αλλά ισχύει και για το =.
Η κλήση x = value αναθέτη το value στο x και στη συνέχεια την επιστρέφει.
Ακολουθεί μια επίδειξη που χρησιμοποιεί μια εργασία ως μέρος μιας πιο περίπλοκης έκφρασης:
let a = 1;
let b = 2;
*!*
let c = 3 - (a = b + 1);
*/!*
alert( a ); // 3
alert( c ); // 0Στο παραπάνω παράδειγμα, το αποτέλεσμα της έκφρασης (a = b + 1) είναι η τιμή που αντιστοιχίστηκε στο a (δηλαδή 3). Στη συνέχεια χρησιμοποιείται για περαιτέρω αξιολογήσεις.
Αστείος ο κώδικας, έτσι δεν είναι; Πρέπει να καταλάβουμε πώς λειτουργεί γιατί μερικές φορές το βλέπουμε σε βιβλιοθήκες της JavaScript.
Ωστόσο, μην γράψετε τον κωδικό έτσι. Τέτοια κόλπα σίγουρα δεν κάνουν τον κώδικα σαφέστερο ή αναγνώσιμο.
Ένα άλλο ενδιαφέρον χαρακτηριστικό είναι η ικανότητα αλυσίδας αναθέσεων:
let a, b, c;
*!*
a = b = c = 2 + 2;
*/!*
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4Οι αλυσοδεμένες εργασίες αξιολογούνται από δεξιά προς τα αριστερά. Αρχικά, αξιολογείται η πιο δεξιά έκφραση 2 + 2 και στη συνέχεια αντιστοιχίζεται στις μεταβλητές στα αριστερά: c, b και a. Στο τέλος, όλες οι μεταβλητές μοιράζονται μία μόνο τιμή.
<<<<<<< HEAD Για άλλη μια φορά, για λόγους αναγνωσιμότητας, είναι καλύτερο να χωρίσετε αυτόν τον κώδικα σε λίγες γραμμές:
Chained assignments evaluate from right to left. First, the rightmost expression 2 + 2 is evaluated and then assigned to the variables on the left: c, b and a. At the end, all the variables share a single value.
Once again, for the purposes of readability it's better to split such code into a few lines:
52c1e61915bc8970a950a3f59bd845827e49b4bf
c = 2 + 2;
b = c;
a = c;Αυτό είναι πιο εύκολο να το διαβάσετε, ειδικά όταν κάνετε γρήγορη σάρωση του κώδικα.
Συχνά πρέπει να εφαρμόσουμε έναν τελεστή σε μια μεταβλητή και να αποθηκεύσουμε το νέο αποτέλεσμα στην ίδια μεταβλητή.
Για παράδειγμα:
let n = 2;
n = n + 5;
n = n * 2;Αυτή η σημειογραφία μπορεί να συντομευτεί χρησιμοποιώντας τους τελεστές += και *=:
let n = 2;
n += 5; // now n = 7 (same as n = n + 5)
n *= 2; // now n = 14 (same as n = n * 2)
alert( n ); // 14Σύντομοι τελεστές "τροποποίησης και ανάθεση" υπάρχουν για όλους τους αριθμητικούς και bitwise τελεστές: /=, -=, κτλπ.
Τέτοιοι τελεστές έχουν την ίδια προτεραιότητα με μια κανονική ανάθεση, οπότε εκτελούν τους περισσότερους άλλους υπολογισμούς:
let n = 2;
n *= 3 + 5; // right part evaluated first, same as n *= 8
alert( n ); // 16 (το σωστό μέρος αξιολογήθηκε πρώτα, το ίδια και για το n *= 8)Η αύξηση ή η μείωση ενός αριθμού κατά έναν είναι από τις πιο κοινές αριθμητικές πράξεις.
Έτσι, υπάρχουν ειδικοί τελεστές για αυτό:
-
Αύξηση
++αυξάνει την μεταβλητή κατά 1:let counter = 2; counter++; // λειτουργεί ακριβώς το ίδια με counter = counter + 1, απλώς είναι συντομογραφία alert( counter ); // 3
-
Μείωσει
--μειώνει την μεταβλητή κατά 1:let counter = 2; counter--; // λειτουργεί ακριβώς το ίδια με counter = counter - 1, απλώς είναι συντομογραφία alert( counter ); // 1
Η αύξηση / μείωση μπορεί να εφαρμοστεί μόνο σε μεταβλητές. Προσπαθώντας να το χρησιμοποιήσετε σε μια τιμή όπως `5++` θα εμφανιστεί ένα σφάλμα.
Οι τελεστές ++ και ``-` μπορούν να τοποθετηθούν είτε πριν είτε μετά από μια μεταβλητή.
- Όταν ο τελεστής ακολουθεί τη μεταβλητή, είναι σε "postfix form":
counter++. - Η "prefix form" είναι όταν ο τελεστής βρίσκεται πριν από τη μεταβλητή:
++counter.
Και οι δύο αυτές δηλώσεις κάνουν το ίδιο πράγμα: αύξηση του counter κατά 1.
Υπάρχει διαφορά; Ναι, αλλά μπορούμε να το δούμε μόνο εάν χρησιμοποιήσουμε την επιστρεφόμενη τιμή ++ / -.
Ας διευκρινίσουμε. Όπως γνωρίζουμε, όλοι οι τελεστές επιστρέφουν μια τιμή. Η αύξηση/μείωση δεν αποτελεί εξαίρεση. Η φόρμα προθέματος επιστρέφει τη νέα τιμή ενώ η φόρμα μετά την επιδιόρθωση επιστρέφει την παλιά τιμή (πριν από την αύξηση/μείωση).
Για να δείτε τη διαφορά εδώ είναι ένα παράδειγμα:
let counter = 1;
let a = ++counter; // (*)
alert(a); // *!*2*/!*Στη γραμμή (*), το prefix φόρμα ++counter αυξάνει το counter και επιστρέφει τη νέα τιμή, 2. Έτσι, το alert δείχνει 2.
Τώρα, ας χρησιμοποιήσουμε τη φόρμα μετά την επιδιόρθωση:
let counter = 1;
let a = counter++; // (*) άλλαξε ++counter σε counter++
alert(a); // *!*1*/!*Στη γραμμή (*), η φόρμα postfix counter++ αυξάνει επίσης τον counter αλλά επιστρέφει την παλιά τιμή (πριν από την αύξηση). Έτσι, το alert δείχνει 1.
Να συνοψίσουμε:
-
Εάν το αποτέλεσμα της αύξησης/μείωσης δεν χρησιμοποιείται, δεν υπάρχει διαφορά σε ποια μορφή θα χρησιμοποιηθεί:
let counter = 0; counter++; ++counter; alert( counter ); // 2, οι παραπάνω γραμμές έκαναν το ίδιο
-
Εάν θέλουμε να αυξήσουμε μια τιμή και να χρησιμοποιήσουμε αμέσως το αποτέλεσμα του τελεστή, χρειαζόμαστε τη φόρμα prefix:
let counter = 0; alert( ++counter ); // 1
-
Εάν θέλουμε να αυξήσουμε μια τιμή αλλά να χρησιμοποιήσουμε την προηγούμενη τιμή, χρειαζόμαστε τη φόρμα μετά την postfix�:
let counter = 0; alert( counter++ ); // 0
Οι τελεστές `++/--` μπορούν να χρησιμοποιηθούν και σε εσωτερικές εκφράσεις. Η προτεραιότητά τους είναι υψηλότερη από τις περισσότερες άλλες αριθμητικές πράξεις.
Για παράδειγμα:
```js run
let counter = 1;
alert( 2 * ++counter ); // 4
```
Σύγκρινε με:
```js run
let counter = 1;
alert( 2 * counter++ ); // 2, επειδή ο counter++ επιστρέφει την "παλιά" τιμή
```
Αν και είναι τεχνικά εντάξει, αυτή η σημειογραφία κάνει συνήθως τον κώδικα λιγότερο ευανάγνωστο. Μια γραμμή κάνει πολλά πράγματα - όχι καλά.
Κατά την ανάγνωση του κώδικα, μια γρήγορη "κάθετη" σάρωση ματιών μπορεί εύκολα να χάσει κάτι όπως το `counter++` και δεν θα είναι προφανές ότι η μεταβλητή αυξήθηκε.
Σας συμβουλεύουμε ένα στυλ "μία γραμμή - μία δράση":
```js run
let counter = 1;
alert( 2 * counter );
counter++;
```
## Τελεστές Bitwise
Οι τελεστές Bitwise αντιμετωπίζουν τα ορίσματα ως ακέραιους αριθμούς 32-bit και εργάζονται στο επίπεδο της δυαδικής τους αναπαράστασης.
Αυτοί οι τελεστές δεν είναι συγκεκριμένοι για JavaScript. Υποστηρίζονται στις περισσότερες γλώσσες προγραμματισμού.
Ο κατάλογος των τελεστών:
- AND ( `&` )
- OR ( `|` )
- XOR ( `^` )
- NOT ( `~` )
- LEFT SHIFT ( `<<` )
- RIGHT SHIFT ( `>>` )
- ZERO-FILL RIGHT SHIFT ( `>>>` )
Αυτοί οι τελεστές χρησιμοποιούνται πολύ σπάνια όταν έχουμε να κάνουμε με αριθμούς στο πολύ χαμηλότερο (bitwise) επίπεδο. Δεν θα χρειαζόμαστε αυτούς τους τελεστές σύντομα, καθώς η ανάπτυξη ιστού τους χρησιμοποιεί ελάχιστα, αλλά σε ορισμένες ειδικές περιοχές, όπως η κρυπτογραφία, είναι χρήσιμες. Μπορείτε να διαβάσετε το άρθρο του [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) στο MDN όταν υπάρχει ανάγκη.
## Κόμμα
Ο τελεστής κόμμα `,` είναι ένας από τους σπανιότερους και πιο ασυνήθιστους τελεστές. Μερικές φορές, χρησιμοποιείται για τη σύνταξη μικρότερου κώδικα, οπότε πρέπει να το γνωρίζουμε για να κατανοήσουμε τι συμβαίνει.
Ο τελεστής κόμμα μας επιτρέπει να αξιολογήσουμε διάφορες εκφράσεις, διαιρώντας τις με κόμμα `,`. Κάθε ένα από αυτά αξιολογείται αλλά επιστρέφεται μόνο το αποτέλεσμα του τελευταίου.
Για παράδειγμα:
```js run
*!*
let a = (1 + 2, 3 + 4);
*/!*
alert( a ); // 7 (το αποτέλεσμα του 3 + 4)
```
Εδώ, αξιολογείται η πρώτη έκφραση `1 + 2` και το αποτέλεσμα απορρίπτεται. Στη συνέχεια, το `3 + 4` αξιολογείται και επιστρέφεται ως αποτέλεσμα.
```smart header="Comma has a very low precedence"
Λάβετε υπόψη ότι ο τελεστής κόμμα έχει πολύ χαμηλή προτεραιότητα, χαμηλότερος από το `=`, οπότε οι παρενθέσεις είναι σημαντικές στο παραπάνω παράδειγμα.
Χωρίς αυτούς: `a = 1 + 2, 3 + 4` αξιολογεί πρώτα το `+`, αθροίζοντας τους αριθμούς σε `a = 3, 7`, στη συνέχεια ο τελεστής ανάθεσης `=` εκχωρεί `a = 3` και το υπόλοιπο αγνοείται. Είναι σαν `(a = 1 + 2), 3 + 4`.
```
Γιατί χρειαζόμαστε έναν τελεστή που αγνοεί τα πάντα εκτός από την τελευταία έκφραση;
Μερικές φορές, οι άνθρωποι το χρησιμοποιούν σε πιο περίπλοκες κατασκευές για να βάλουν πολλές ενέργειες σε μία γραμμή.
Για παραδείγμα:
```js
// τρεις λειτουργίες σε μία γραμμή
for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) {
...
}
```
Τέτοια κόλπα χρησιμοποιούνται σε πολλά frameworks της JavaScript. Γι 'αυτό τα αναφέρουμε. Αλλά συνήθως, δεν βελτιώνουν την αναγνωσιμότητα του κώδικα, οπότε πρέπει να σκεφτούμε καλά πριν τα χρησιμοποιήσουμε.