Εισαγωγή στη σύγχρονη JavaScript

Κώδικας, πληροφορίες, ερωτήσεις και απαντήσεις σχετικές με την JavaScript.

Συντονιστές: WebDev Moderators, Super-Moderators

Απάντηση
Άβαταρ μέλους
skeftomilos
Script Master
Δημοσιεύσεις: 2888
Εγγραφή: 07 Ιαν 2005 07:22
Τοποθεσία: Αθήνα

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από skeftomilos » 13 Μαρ 2006 04:49

Τι θα λέγατε για ένα JavaScript tutorial γραμμένο από έναν guru της γλώσσας; Αν μάλιστα σας έλεγα ότι είναι δημοσιευμένο μόλις μια εβδομάδα πριν, κι έγινε αμέσως ανάρπαστο στο delicious; Οι αλλαγές που έχουν συντελεστεί τον τελευταίο καιρό στη φιλοσοφία αυτής της γλώσσας κάνουν τα παλιά tutorials να μοιάζουν απαρχαιωμένα στην καλύτερη περίπτωση, άχρηστα και επιβλαβή στη χειρότερη. Ο Simon Willison έγραψε το εν λόγω κείμενο στα πλαίσια μιας τρίωρης ομιλίας του, και είναι πολύ κατάλληλο για όσους γνωρίζουν προγραμματισμό και θέλουν να αποκτήσουν μερικές γερές βάσεις στη JavaScript. Όσοι το διαβάσετε θα μάθετε πράγματα που δε θα βρείτε σε βιβλία 1000 σελίδων, και επιπλέον θα ενημερωθείτε για τα IN και OUT του σύγχρονου web scripting. Το tutorial είναι εδώ: A (Re)-Introduction to JavaScript.

Όσοι από εσάς θα το προτιμούσατε στα ελληνικά, να το:
____________________________________________________________

Εισαγωγή

Το όνομά μου είναι Simon Willison και ο τίτλος αυτής της παρουσίασης είναι «Επαν-εισαγωγή στη JavaScript».

Γιατί χρειάζεται μια νέα εισαγωγή; Διότι η JavaScript μπορεί δίκαια να ισχυριστεί ότι είναι η πιο παρεξηγημένη γλώσσα του κόσμου. Αν και συχνά γίνεται αντικείμενο χλευασμού και χαρακτηρίζεται ως παιχνίδι, πίσω από την απατηλή της απλότητα κρύβονται μερικά ισχυρά γλωσσικά στοιχεία. Ο χρόνος που πέρασε είδε να ξεφυτρώνουν ένας αριθμός από εφαρμογές JavaScript μεγάλης εμβέλειας, δείχνοντας ότι η βαθύτερη γνώση αυτής της τεχνολογίας είναι ένα σημαντικό προσόν για κάθε web developer.

Είναι χρήσιμο να αρχίσουμε με μια ιδέα από την ιστορία της γλώσσας. Η JavaScript δημιουργήθηκε το 1995 από τον Brendan Eich, έναν μηχανικό της Netscape, και εκδόθηκε με τον Netscape 2 στις αρχές του 1996. Το αρχικό της όνομα ήταν LiveScript, αλλά μια ατυχής απόφαση μάρκετινγκ την μετονόμασε σε JavaScript. Το σκεπτικό ήταν να κεφαλαιοποιηθεί η δημοτικότητα της γλώσσας Java της Sun, παρότι οι δύο γλώσσες έχουν ελάχιστα κοινά στοιχεία μεταξύ τους. Αυτό αποτελεί μόνιμη πηγή σύγχυσης από τότε μέχρι σήμερα.

Μερικούς μήνες αργότερα η Microsoft παρουσίασε μια σε γενικές γραμμές συμβατή έκδοση της γλώσσας με το όνομα JScript (μαζί με τον IE3). Η Netscape υπέβαλλε τη γλώσσα στον ECMA International, έναν Ευρωπαϊκό οργανισμό προτύπων, μια πρωτοβουλία που κατέληξε στην πρώτη έκδοση της EcmaScript το 1997. Το πρότυπο έφτασε στην έκδοση 3 το 1999 και από τότε παρέμεινε γενικά αμετάβλητο, αν και η έκδοση 4 βρίσκεται σε φάση ανάπτυξης.

Αυτή η σταθερότητα υπήρξε καλοδεχούμενη από τους developers, καθώς έδωσε χρόνο στις διάφορες υλοποιήσεις να προσαρμοστούν. Θα εστιάσω σχεδόν αποκλειστικά στη διάλεκτο της έκδοσης 3. Στο εξής θα παραμείνω στον όρο JavaScript για λόγους οικειότητας.

Αντίθετα από τις περισσότερες γλώσσες προγραμματισμού η γλώσσα JavaScript δεν έρχεται εφοδιασμένη με δυνατότητες εισόδου/εξόδου. Είναι σχεδιασμένη ως γλώσσα scripting σε ένα περιβάλλον που τη φιλοξενεί, και η ύπαρξη μηχανισμών επικοινωνίας με τον έξω κόσμο είναι ευθύνη αυτού του περιβάλλοντος. Το πιο συνηθισμένο περιβάλλον φιλοξενίας είναι ο browser, αλλά interpreters JavaScript μπορούν να βρεθούν στον Acrobat της Adobe, το Photoshop, τη μηχανή Widget της Yahoo! και αλλού.


Γενική Εικόνα

Ας αρχίσουμε κοιτάζοντας το δομικό λίθο κάθε γλώσσας, τους τύπους. Τα προγράμματα JavaScript χειρίζονται τιμές, και όλες αυτές οι τιμές ανήκουν σε έναν τύπο. Οι τύποι τις JavaScript είναι:

- Numbers
- Strings
- Booleans
- Functions
- Objects

...α, και Undefined και Null, που είναι κάπως παράξενοι τύποι. Και Arrays, που είναι ειδικού τύπου αντικείμενα. Και Dates και Regular Expressions που είναι αντικείμενα που παίρνετε δωρεάν. Και για να είμαι τεχνικά ακριβής οι functions δεν είναι παρά ειδικού τύπου αντικείμενα. Επομένως το διάγραμμα των τύπων μοιάζει περισσότερο με αυτό:

- Number
- String
- Boolean
- Object
--- Function
--- Array
--- Date
--- RegExp
- Null
- Undefined

Και υπάρχει επίσης κι ένας ενσωματωμένος τύπος Error. Ωστόσο τα πράγματα είναι πολύ ευκολότερα αν παραμείνουμε στο πρώτο διάγραμμα.


Αριθμοί

Οι αριθμοί στη JavaScript είναι «τιμές διπλής-ακρίβειας 64-bit μορφής IEEE 754», σύμφωνα με τις προδιαγραφές. Αυτό έχει μερικές ενδιαφέρουσες συνέπειες. Δεν υπάρχουν ακέραιοι στη JavaScript, επομένως πρέπει να είστε λίγο προσεκτικοί με την αριθμητική σας αν έχετε συνηθίσει τα μαθηματικά της Java ή της C. Να είστε σε εγρήγορση για καταστάσεις σαν:

Κώδικας: Επιλογή όλων

0.1 + 0.2 = 0.30000000000000004
Υποστηρίζονται οι τυπικοί αριθμητικοί τελεστές, που περιλαμβάνουν πρόσθεση, αφαίρεση, υπόλοιπο ακέραιας διαίρεσης και λοιπά. Υπάρχει επίσης ένα ενσωματωμένο αντικείμενο που ξέχασα να αναφέρω προηγούμενα και λέγετε Math για το χειρισμό πιο προχωρημένων μαθηματικών συναρτήσεων και σταθερών:

Κώδικας: Επιλογή όλων

Math.sin(3.5);
d = Math.PI * r * r;
Μπορείτε να μετατρέψετε έναν αριθμό χρησιμοποιώντας την ενσωματωμένη συνάρτηση parseInt(). Αυτή δέχεται την αριθμητική βάση της μετατροπής ως προαιρετικό δεύτερο όρισμα, το οποίο θα πρέπει να παρέχετε πάντα:

Κώδικας: Επιλογή όλων

> parseInt("123")
123
> parseInt("010")
8
Αυτό συνέβη γιατί η συνάρτηση parseInt αποφάσισε να χειριστεί το string σαν οκταδικό αριθμό λόγω του αρχικού ψηφίου 0. Αν φροντίσετε να ορίζετε πάντα τη βάση μπορείτε να αποφύγετε εντελώς αυτό το πρόβλημα:

Κώδικας: Επιλογή όλων

> parseInt("123", 10)
123
> parseInt("010", 10)
10
Αν θέλετε να μετατρέψετε ένα δυαδικό αριθμό σε δεκαδικό απλά αλλάξτε τη βάση:

Κώδικας: Επιλογή όλων

> parseInt("11", 2)
3
Η JavaScript έχει επίσης μία ειδική τιμή που λέγεται Not-a-Number. Αυτή κάνει την εμφάνισή της όταν προσπαθήσετε να κάνετε πράγματα όπως να μετατρέψετε μια μη-αριθμητική τιμή σε αριθμό.

Κώδικας: Επιλογή όλων

> parseInt("hello", 10)
NaN
Η τιμή NaN είναι τοξική. Αν τη δώσετε ως είσοδο σε οποιαδήποτε αριθμητική πράξη το αποτέλεσμα θα είναι επίσης NaN:

Κώδικας: Επιλογή όλων

> NaN + 5
NaN
Μπορείτε να την ανιχνεύσετε χρησιμοποιώντας την ενσωματωμένη συνάρτηση isNaN:

Κώδικας: Επιλογή όλων

> isNaN(NaN)
true
Η JavaScript έχει επίσης ειδικές τιμές για το θετικό και αρνητικό άπειρο.

Κώδικας: Επιλογή όλων

> 1 / 0
Infinity
> -1 / 0
-Infinity

Strings

Τα strings στη JavaScript είναι ακολουθίες χαρακτήρων. Για την ακρίβεια είναι ακολουθίες χαρακτήρων unicode, με κάθε χαρακτήρα να αντιστοιχεί σε ένα αριθμό 16 bit. Αυτό θα πρέπει να ακούγεται ευχάριστα στα αυτιά οποιουδήποτε ασχολήθηκε ποτέ με internationalization.

Όταν χρειάζεστε ένα μεμονωμένο χαρακτήρα απλά χρησιμοποιείστε ένα string με μήκος 1.

Για να βρείτε το μήκος ενός string, χρησιμοποιείστε την ιδιότητα length.

Κώδικας: Επιλογή όλων

> "hello".length
5
Να η πρώτη μας επαφή με τα αντικείμενα JavaScript! Αλήθεια, ανέφερα ότι και τα strings είναι αντικείμενα; Έχουν και μεθόδους:

Κώδικας: Επιλογή όλων

> "hello".charAt(0)
h
> "hello, world".replace("hello", "goodbye")
goodbye, world
> "hello".toUpperCase()
HELLO

Άλλοι τύποι

Το null σημαίνει μία εσκεμμένη μη-τιμή. Το undefined σημαίνει ότι δεν έχει ακόμα αποδοθεί κάποια τιμή. Θα μιλήσουμε για τις μεταβλητές αργότερα, αλλά στη JavaScript είναι δυνατό να ορίσουμε μια μεταβλητή χωρίς να της δώσουμε τιμή. Αν το κάνουμε αυτό η τιμή της μεταβλητής είναι η τιμή «undefined».

Η JavaScript έχει ένα τύπο boolean, ο οποίος είναι είτε true είτε false (και τα δύο είναι keywords). Οτιδήποτε άλλο στη γλώσσα θεωρείται είτε αληθές είτε ψευδές, το οποίο προσδιορίζει τι συμβαίνει όταν χρησιμοποιηθεί σε θέση boolean. Οι κανόνες περί αλήθειας και ψεύδους είναι οι εξής:

0, "", NaN, null, και undefined είναι ψευδή. Οτιδήποτε άλλο είναι αληθές.

Υποστηρίζονται τελεστές boolean όπως &&, || και !. Μπορείτε να μετατρέψετε κάθε τιμή σε boolean εφαρμόζοντας δύο φορές τον τελεστή άρνησης:

Κώδικας: Επιλογή όλων

> !!""
false
> !!234
true

Μεταβλητές

Οι μεταβλητές στη JavaScript δηλώνονται με τη χρήση του keyword var:

Κώδικας: Επιλογή όλων

var a;
var name = "simon";
Αν δηλώσετε μια μεταβλητή χωρίς να της αποδώσετε κάτι, η τιμή της είναι undefined.


Τελεστές

Οι αριθμητικοί τελεστές της JavaScript είναι οι +, -, *, / και %, το οποίο είναι το υπόλοιπο ακέραιας διαίρεσης. Οι τιμές αποδίδονται με =, και υπάρχουν επίσης σύνθετες εντολές απόδοσης όπως += και -= οι οποίες αναλύονται σε x = x τελεστής y.

Κώδικας: Επιλογή όλων

x += 5
x = x + 5
Μπορείτε να χρησιμοποιήσετε τους ++ και -- για άυξηση και μείωση αντίστοιχα. Αυτοί μπορούν να τοποθετηθούν πριν ή μετά τη μεταβλητή (prefix/postfix operators).

Ο τελεστής + κάνει επιπλέον και συνένωση string:

Κώδικας: Επιλογή όλων

> "hello" + " world"
hello world
Αν προσθέσετε έναν αριθμό κι ένα string (ή άλλη τιμή) τα πάντα μετατρέπονται σε string πριν την πράξη. Αυτό μπορεί να σας συλλάβει εξ απροόπτου:

Κώδικας: Επιλογή όλων

> "3" + 4 + 5
345
> 3 + 4 + "5"
75
Ένας χρήσιμος τρόπος να μετατρέψετε κάτι σε string είναι να του προσθέσετε το κενό string.

Οι συγκρίσεις στη JavaScript μπορούν να γίνουν με χρήση των <, >, <= και >=. Αυτοί λειτουργούν εξίσου με strings και αριθμούς. Η ισότητα είναι ελαφρά πιο περίπλοκη. Ο τελεστής == πραγματοποιεί μετατροπή τύπου αν του δώσετε διαφορετικούς τύπους, με ενίοτε ενδιαφέροντα αποτελέσματα:

Κώδικας: Επιλογή όλων

> "dog" == "dog"
true
> 1 == true
true
Αν αυτές οι μετατροπές είναι ανεπιθύμητες χρησιμοποιήστε τον τελεστή τριπλό-ίσον:

Κώδικας: Επιλογή όλων

> 1 === true
false
> true === true
true
Υπάρχουν επίσης τελεστές != και !==.

Η JavaScript έχει ακόμα και δυαδικούς τελεστές. Αν ποτέ τους επιθυμήσετε, εκεί είναι.


Δομές Ελέγχου

Η JavaScript έχει ένα παραπλήσιο πακέτο δομών ελέγχου με άλλες γλώσσες της οικογένειας C. Οι εκτέλεση εντολών κατά συνθήκη υποστηρίζεται με τα if και else. Μπορείτε να σχηματίσετε αλυσίδες απ' αυτά αν θέλετε:

Κώδικας: Επιλογή όλων

var name = "kittens";
if &#40;name == "puppies"&#41; &#123;
  name += "!";
&#125; else if &#40;name == "kittens"&#41; &#123;
  name += "!!";
&#125; else &#123;
  name = "!" + name;
&#125;
name == "kittens!!"
Η JavaScript έχει βρόγχους while και do-while. Ο δεύτερος είναι καλός όταν θέλουμε να είμαστε σίγουροι πως το σώμα του βρόγχου θα εκτελεστεί τουλάχιστον μία φορά:

Κώδικας: Επιλογή όλων

while &#40;true&#41; &#123;
  // an infinite loop!
&#125;

do &#123;
  var input = get_input&#40;&#41;;
&#125; while &#40;inputIsNotValid&#40;input&#41;&#41;
Ο βρόγχος for της JavaScript είναι ο ίδιος όπως στη C και τη Java, σας επιτρέπει να ορίσετε όλα τα δεδομένα ελέγχου του βρόγχου σε μία μόνο γραμμή.

Κώδικας: Επιλογή όλων

for &#40;var i = 0; i < 5; i++&#41; &#123;
  // Will execute 5 times
&#125;
Οι τελεστές && και || χρησιμοποιούν λογική παράκαμψης (short-circuit), το οποίο σημαίνει ότι η εκτέλεση του δεύτερου τελεστέου θα εξαρτηθεί από τον πρώτο. Αυτό είναι χρήσιμο για τον έλεγχο του null πριν την προσπέλαση ιδιοτήτων αντικειμένων.

Κώδικας: Επιλογή όλων

var name = o && o.getName&#40;&#41;;
Ή για τον καθορισμό προεπιλεγμένων τιμών:

Κώδικας: Επιλογή όλων

var name = otherName || "default";
Η JavaScript έχει ένα τριπλό τελεστή για κατά συνθήκη εντολές της μίας γραμμής:

Κώδικας: Επιλογή όλων

var allowed = &#40;age > 18&#41; ? "yes" &#58; "no";
Η εντολή switch μπορεί να χρησιμοποιηθεί για πολλαπλές διακλαδώσεις με βάση έναν αριθμό ή string:

Κώδικας: Επιλογή όλων

switch&#40;action&#41; &#123;
    case 'draw'&#58;
        drawit&#40;&#41;;
        break;
    case 'eat'&#58;
        eatit&#40;&#41;;
        break;
    default&#58;
        donothing&#40;&#41;;
&#125;
Αν δεν προσθέσετε την εντολή break η εκτέλεση θα συνεχιστεί στο επόμενο επίπεδο. Αυτό πολύ σπάνια είναι επιθυμητό, στην πραγματικότητα είναι καλό να αφήσετε ένα σχόλιο αν σκόπιμα παραλείψετε το break, ώστε να διευκολύνετε το debugging:

Κώδικας: Επιλογή όλων

switch&#40;a&#41; &#123;
    case 1&#58; // fallthrough
    case 2&#58;
        eatit&#40;&#41;;
        break;
    default&#58;
        donothing&#40;&#41;;
&#125;
Ο όρος default είναι προαιρετικός. Μπορείτε να έχετε εκφράσεις τόσο στο switch όσο και στα cases αν θέλετε. Οι συγκρίσεις μεταξύ των δύο γίνονται με τον τελεστή === (αυστηρή ισότητα):

Κώδικας: Επιλογή όλων

switch&#40;1 + 3&#41;&#58;
    case 2 + 2&#58;
        yay&#40;&#41;;
        break;
    default&#58;
        neverhappens&#40;&#41;;
&#125;

Αντικείμενα

Τα αντικείμενα της JavaScript είναι απλά συλλογές από ζευγάρια Όνομα/Τιμή. Σαν τέτοια είναι παρεμφερή με:

- Dictionaries στην Python
- Hashes στις Perl και Ruby
- Hash tables στις C και C++
- HashMaps στη Java
- Associative arrays στην PHP

Το γεγονός ότι αυτή η δομή δεδομένων είναι τόσο πλατιά διαδεδομένη είναι χειροπιαστή απόδειξη της προσαρμοστικότητάς της. Εφόσον οτιδήποτε πέρα από τους στοιχειώδεις τύπους στη JavaScript είναι αντικείμενο, κάθε πρόγραμμα JavaScript συνεπάγεται ένα μεγάλο αριθμό από αναζητήσεις σε hash tables. Είναι καλό που αυτές είναι τόσο γρήγορες!

Το τμήμα «Όνομα» είναι ένα string, ενώ το τμήμα «Τιμή» μπορεί να είναι κάθε τιμή JavaScript, συμπεριλαμβανομένων άλλων αντικειμένων. Αυτό σας επιτρέπει να χτίζετε δομές δεδομένων οποιουδήποτε βαθμού πολυπλοκότητας.

Υπάρχουν δύο βασικοί τρόποι να δημιουργηθεί ένα αντικείμενο:

Κώδικας: Επιλογή όλων

var obj = new Object&#40;&#41;;
Και:

Κώδικας: Επιλογή όλων

var obj = &#123;&#125;;
Αυτοί είναι σημασιολογικά ισοδύναμοι. Ο δεύτερος ονομάζεται σύνταξη literal και είναι πιο βολικός. Η σύνταξη literal δεν υπήρχε στις πολύ πρώιμες εκδόσεις της γλώσσας, και αυτός είναι ο λόγος που βλέπετε τόσο πολύ κώδικα να χρησιμοποιεί την παλιά μέθοδο.

Άπαξ και δημιουργήθηκε, οι ιδιότητες του αντικειμένου μπορούν να προσπελαστούν πάλι με δύο τρόπους:

Κώδικας: Επιλογή όλων

obj.name = "Simon"
var name = obj.name;
Και...

Κώδικας: Επιλογή όλων

obj&#91;"name"&#93; = "Simon";
var name = obj&#91;"name"&#93;;
Αυτοί είναι επίσης σημασιολογικά ισοδύναμοι. Ο δεύτερος τρόπος έχει το πλεονέκτημα ότι το όνομα της ιδιότητας παρέχεται ως string, που σημαίνει ότι μπορεί να υπολογιστεί κατά το χρόνο εκτέλεσης. Μπορεί επίσης να χρησιμοποιηθεί για την ανάγνωση και απόδοση τιμής σε ιδιότητες με ονόματα που είναι δεσμευμένες λέξεις:

Κώδικας: Επιλογή όλων

obj.for = "Simon"; // Συντακτικό σφάλμα
obj&#91;"for"&#93; = "Simon"; // Δουλεύει θαυμάσια
Η σύνταξη literal μπορεί να χρησιμοποιηθεί για να αρχικοποιήσει ένα αντικείμενο στην ολότητά του:

Κώδικας: Επιλογή όλων

var obj = &#123;
    name&#58; "Carrot",
    "for"&#58; "Max",
    details&#58; &#123;
        color&#58; "orange",
        size&#58; 12
    &#125;
&#125;
Είναι δυνατή η δημιουργία αλυσίδων από προσπελάσεις ιδιοτήτων:

Κώδικας: Επιλογή όλων

> obj.details.color
orange
> obj&#91;"details"&#93;&#91;"size"&#93;
12

Arrays

Τα Arrays στη JavaScript είναι απλά ένα ειδικός τύπος αντικειμένου, όπου ως ονόματα ιδιοτήτων αντί για strings γίνετε χρήση αριθμών. Λειτουργούν σε μεγάλο βαθμό ως κανονικά αντικείμενα (που προσπελαύνονται πάντα με τη σύνταξη []) αλλά έχουν μία μαγική ιδιότητα με όνομα length. Αυτή είναι πάντα κατά μια μονάδα μεγαλύτερη από το μεγαλύτερο δείκτη του array.

Ο παλιός τρόπος δημιουργίας arrays έχει ως εξής:

Κώδικας: Επιλογή όλων

> var a = new Array&#40;&#41;;
> a&#91;0&#93; = "dog";
> a&#91;1&#93; = "cat";
> a&#91;2&#93; = "hen";
> a.length
3
Μια πιο βολική σύνταξη είναι τα array literals:

Κώδικας: Επιλογή όλων

> var a = &#91;"dog", "cat", "hen"&#93;;
> a.length
3
Το να αφήσετε ένα πλεονάζον κόμμα στο τέλος του array literal ερμηνεύεται διαφορετικά από browser σε browser, γι αυτό μην το κάνετε.

Σημειώστε πως η array.length δεν είναι πάντα ο αριθμός στοιχείων του array. Δείτε το παρακάτω:

Κώδικας: Επιλογή όλων

> var a = &#91;"dog", "cat", "hen"&#93;;
> a&#91;100&#93; = "fox";
> a.length
101
Θυμηθείτε, το length του array είναι μια μονάδα παραπάνω από το μεγαλύτερο δείκτη.

Αν ζητήσετε έναν μη-υπαρκτό δείκτη array, θα πάρετε undefined:

Κώδικας: Επιλογή όλων

> typeof&#40;a&#91;90&#93;&#41;&#93;
undefined
Λαμβάνοντας υπόψη τα παραπάνω, μπορείτε να διατρέξετε ένα array χρησιμοποιώντας το εξής:

Κώδικας: Επιλογή όλων

for &#40;var i = 0; i < a.length; i++&#41; &#123;
    // Do something with a&#91;i&#93;
&#125;
Αυτό είναι ελαφρά αναποτελεσματικό καθώς ζητάτε την ιδιότητα length μια φορά σε κάθε βρόγχο. Μια βελτίωση είναι η εξής:

Κώδικας: Επιλογή όλων

for &#40;var i = 0, j = a.length; i < j; i++&#41; &#123;
    // Do something with a&#91;i&#93;
&#125;
Ένα ακόμα ωραιότερο ιδίωμα είναι:

Κώδικας: Επιλογή όλων

for &#40;var i = 0, item; item = a&#91;i&#93;; i++&#41; &#123;
    // Do something with item
&#125;
Εδώ θέτουμε δύο μεταβλητές. Στο μέσο της εντολής for συμβαίνει μια απόδοση τιμής και ταυτόχρονα ένας έλεγχος αλήθειας. Αν η απόδοση επιτύχει, ο βρόγχος συνεχίζει. Εφόσον το i αυξάνεται κάθε φορά, τα περιεχόμενα του array θα αποδοθούν στο item το ένα μετά το άλλο. Ο βρόγχος τερματίζει όταν βρεθεί ένα ψευδές στοιχείο (όπως είναι το undefined).

Σημειώστε πως αυτό το trick θα πρέπει να χρησιμοποιείται μόνο για arrays που γνωρίζετε ότι δεν περιέχουν ψευδείς τιμές (για παράδειγμα arrays αντικειμένων ή κόμβων DOM). Αν διατρέχετε αριθμητικά δεδομένα που μπορεί να περιλαμβάνουν το 0 ή strings που μπορεί να περιλαμβάνουν το κενό string, χρησιμοποιήστε το ιδίωμα i, j.

Αν θέλετε να προσθέσετε ένα στοιχείο σε ένα array, ο ασφαλέστερος τρόπος είναι αυτός:

Κώδικας: Επιλογή όλων

a&#91;a.length&#93; = item;
Καθώς η a.length είναι μια μονάδα μεγαλύτερη από τον μεγαλύτερο δείκτη, μπορείτε να είστε σίγουροι ότι κάνετε απόδοση σε μία κενή θέση στο τέλος του array.

Τα arrays έρχονται εφοδιασμένα μα αρκετές μεθόδους:

a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)

- η concat επιστρέφει ένα νέο array που περιέχει και τα στοιχεία που προστέθηκαν.
- η pop αφαιρεί και επιστρέφει το τελευταίο στοιχείο.
- η push προσθέτει ένα ή περισσότερα στοιχεία στο τέλος (όπως το ιδίωμα ar[ar.length]).
- η slice επιστρέφει ένα τμήμα του array.
- η sort ταξινομεί και δέχεται ως προαιρετικό όρισμα μία συνάρτηση για τις συγκρίσεις.
- η splice μεταβάλλει ένα array διαγράφοντας ένα τμήμα και αντικαθιστώντας το με άλλα στοιχεία.
- η unshift προσθέτει στοιχεία στην αρχή του array.


Συναρτήσεις

Μαζί με τα αντικείμενα, οι συναρτήσεις είναι το βασικό συστατικό στην κατανόηση της JavaScript. Η πιο στοιχειώδης συνάρτηση δε θα μπορούσε να είναι πολύ απλούστερη απ' αυτήν εδώ:

Κώδικας: Επιλογή όλων

function add&#40;x, y&#41; &#123;
    var total = x + y;
    return total;
&#125;
Αυτή περιγράφει οτιδήποτε υπάρχει για να μάθει κανείς σχετικά με τις βασικές συναρτήσεις. Μια συνάρτηση JavaScript μπορεί να λάβει 0 ή περισσότερες επώνυμες παραμέτρους. Το σώμα της μπορεί να περιέχει όσες εντολές θέλετε, και μπορείτε να ορίσετε τις δικές τις μεταβλητές οι οποίες είναι τοπικές σε αυτή τη συνάρτηση. Η εντολή return μπορεί να χρησιμοποιηθεί ανά πάσα στιγμή για την επιστροφή μίας τιμής, τερματίζοντας τη συνάρτηση. Αν δε χρησιμοποιηθεί εντολή return (ή χρησιμοποιηθεί χωρίς τιμή), η JavaScript επιστρέφει undefined.

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

Κώδικας: Επιλογή όλων

> add&#40;&#41;
Nan // Δε μπορείτε να κάνετε πρόσθεση με το undefined
Μπορείτε ακόμα να περάσετε περισσότερα ορίσματα απ' όσα περιμένει η συνάρτηση:

Κώδικας: Επιλογή όλων

> add&#40;2, 3, 4&#41;
5 // προστέθηκαν τα πρώτα δύο, το 4 αγνοήθηκε.
Αυτό ίσως μοιάζει λίγο χαζό, αλλά οι συναρτήσεις έχουν πρόσβαση σε μια επιπλέον μεταβλητή μέσα στο σώμα τους που λέγετε arguments, η οποία είναι ένα αντικείμενο σαν array που κρατά όλες τις τιμές που περάστηκαν στη συνάρτηση. Ας ξαναγράψουμε τη συνάρτηση add έτσι ώστε να μπορεί να δεχτεί κάθε αριθμό ορισμάτων:

Κώδικας: Επιλογή όλων

function add&#40;&#41; &#123;
    var sum = 0;
    for &#40;var i = 0, j = arguments.length; i < j; i++&#41; &#123;
        sum += arguments&#91;i&#93;;
    &#125;
    return sum;
&#125;

> add&#40;2, 3, 4, 5&#41;
14
Αυτή ωστόσο δεν είναι περισσότερο χρήσιμη απ' το να γράφαμε 2 + 3 + 4 + 5. Ας φτιάξουμε μία συνάρτηση για τον υπολογισμό του μέσου όρου:

Κώδικας: Επιλογή όλων

function avg&#40;&#41; &#123;
    var sum = 0;
    for &#40;var i = 0, j = arguments.length; i < j; i++&#41; &#123;
        sum += arguments&#91;i&#93;;
    &#125;
    return sum / arguments.length;
&#125;
> avg&#40;2, 3, 4, 5&#41;
3.5
Αυτή είναι αρκετά χρήσιμη, αλλά εγκαινιάζει ένα νέο πρόβλημα. Η συνάρτηση avg() δέχεται μία λίστα ορισμάτων χωρισμένη με κόμμα, αλλά τι θα κάνατε αν θέλατε το μέσο όρο ενός array; Θα μπορούσατε απλά να ξαναγράψετε τη συνάρτηση ως εξής:

Κώδικας: Επιλογή όλων

function avgArray&#40;arr&#41; &#123;
    var sum = 0;
    for &#40;var i = 0, j = arr.length; i < j; i++&#41; &#123;
        sum += arr&#91;i&#93;;
    &#125;
    return sum / arr.length;
&#125;
> avgArray&#40;&#91;2, 3, 4, 5&#93;&#41;
3.5
Αλλά θα ήταν ωραίο να μπορούσαμε να ξαναχρησιμοποιήσουμε τη ρουτίνα που μόλις φτιάξαμε. Ευτυχώς η JavaScript σας επιτρέπει να καλέσετε μια συνάρτηση δίνοντας ένα array με ορίσματα, χρησιμοποιώντας τη μέθοδο apply() οποιουδήποτε αντικειμένου function.

Κώδικας: Επιλογή όλων

> avg.apply&#40;null, &#91;2, 3, 4, 5&#93;&#41;
3.5
Το δεύτερο όρισμα της apply() είναι το array που αναμένεται να περιέχει τα ορίσματα. Το πρώτο θα συζητηθεί παρακάτω. Η έμφαση είναι στο γεγονός ότι και οι συναρτήσεις είναι αντικείμενα.

Η JavaScript σας επιτρέπει να δημιουργήσετε ανώνυμες συναρτήσεις.

Κώδικας: Επιλογή όλων

var avg = function&#40;&#41; &#123;
    var sum = 0;
    for &#40;var i = 0, j = arguments.length; i < j; i++&#41; &#123;
        sum += arguments&#91;i&#93;;
    &#125;
    return sum / arguments.length;
&#125;
Αυτή η μορφή είναι σημασιολογικά ισοδύναμη με τη μορφή function avg(). Είναι εξαιρετικά ισχυρή, καθώς σας επιτρέπει να βάλετε μια πλήρη συνάρτηση εκεί που κανονικά θα βάζατε μία έκφραση. Αυτό επιτρέπει κάθε είδους έξυπνα τρικ. Να ένας τρόπος απόκρυψης μερικών τοπικών μεταβλητών, όπως το block scope στη C:

Κώδικας: Επιλογή όλων

> var a = 1;
> var b = 2;
> &#40;function&#40;&#41; &#123;
    var b = 3;
    a += b;
&#125;&#41;&#40;&#41;;
> a
4
> b
2
Η JavaScript σας επιτρέπει να κάνετε αναδρομική κλήση συναρτήσεων. Αυτό είναι ιδιαίτερα χρήσιμο όταν έχετε να κάνετε με δομές δέντρου, όπως αυτές που παίρνετε από το DOM των browsers.

Κώδικας: Επιλογή όλων

function countChars&#40;elm&#41; &#123;
    if &#40;elm.nodeType == 3&#41; &#123; // TEXT_NODE
        return elm.nodeValue.length;
    &#125;
    var count = 0;
    for &#40;var i = 0, child; child = elm.childNodes&#91;i&#93;; i++&#41; &#123;
        count += countChars&#40;child&#41;;
    &#125;
    return count;
&#125;
Αυτό αναδεικνύει ένα δυνητικό πρόβλημα με τις ανώνυμες συναρτήσεις: πώς μπορείτε να τις καλέσετε αναδρομικά αν δε γνωρίζετε το όνομά τους; Η απάντηση βρίσκεται στο αντικείμενο arguments, το οποίο εκτός από το να λειτουργεί ως λίστα ορισμάτων παρέχει επιπλέον μια ιδιότητα με όνομα arguments.callee. Αυτή αναφέρεται πάντα στην τρέχουσα συνάρτηση, και ιδού πως μπορεί με τη βοήθειά της να γίνει εφικτή η αναδρομική κλήση:

Κώδικας: Επιλογή όλων

var charsInBody = &#40;function&#40;elm&#41; &#123;
    if &#40;elm.nodeType == 3&#41; &#123; // TEXT_NODE
        return elm.nodeValue.length;
    &#125;
    var count = 0;
    for &#40;var i = 0, child; child = elm.childNodes&#91;i&#93;; i++&#41; &#123;
        count += arguments.callee&#40;child&#41;;
    &#125;
    return count;
&#125;&#41;&#40;document.body&#41;;
Εφόσον η arguments.callee είναι η τρέχουσα συνάρτηση και όλες οι συναρτήσεις είναι αντικείμενα, μπορείτε να χρησιμοποιήσετε την arguments.callee για να διατηρήσετε δεδομένα από τη μία κλήση της συνάρτησης στην επόμενη. Να μια συνάρτηση που θυμάται πόσες φορές έχει κληθεί:

Κώδικας: Επιλογή όλων

function counter&#40;&#41; &#123;
    if &#40;!arguments.callee.count&#41; &#123;
        arguments.callee.count = 0;
    &#125;
    return arguments.callee.count++;
&#125;

> counter&#40;&#41;
0
> counter&#40;&#41;
1
> counter&#40;&#41;
2

Μεθόδοι Αντικείμενων

Στον κλασικό αντικειμενοστραφή προγραμματισμό τα αντικείμενα είναι συλλογές δεδομένων και μεθόδων που ενεργούν πάνω σε αυτά τα δεδομένα. Ας θεωρήσουμε ένα αντικείμενο person με πεδία μικρό όνομα κι επώνυμο. Υπάρχουν δύο τρόποι εμφάνισης του πλήρους ονόματος: ως «first last» ή ως «last, first». Να ένας τρόπος να γίνει αυτό χρησιμοποιώντας τις συναρτήσεις και τα αντικείμενα που συζητήσαμε προηγουμένως:

Κώδικας: Επιλογή όλων

function makePerson&#40;first, last&#41; &#123;
    return &#123;
        first&#58; first,
        last&#58; last
    &#125;
&#125;
function personFullName&#40;person&#41; &#123;
    return person.first + ' ' + person.last;
&#125;
function personFullNameReversed&#40;person&#41; &#123;
    return person.last + ', ' + person.first
&#125;
> s = makePerson&#40;"Simon", "Willison"&#41;;
> personFullName&#40;s&#41;
Simon Willison
> personFullNameReversed&#40;s&#41;
Willison, Simon
Αυτό λειτουργεί, αλλά δεν είναι πολύ όμορφο. Θα καταλήξετε με ντουζίνες συναρτήσεων στο global namespace. Αυτό που θέλουμε στ' αλήθεια είναι ένας τρόπος να προσδέσουμε μια συνάρτηση σε ένα αντικείμενο. Μιας και οι συναρτήσεις είναι αντικείμενα, αυτό είναι εύκολο:

Κώδικας: Επιλογή όλων

function makePerson&#40;first, last&#41; &#123;
    return &#123;
        first&#58; first,
        last&#58; last,
        fullName&#58; function&#40;&#41; &#123;
            return this.first + ' ' + this.last;
        &#125;,
        fullNameReversed&#58; function&#40;&#41; &#123;
            return this.last + ', ' + this.first;
        &#125;
    &#125;
&#125;
> s = makePerson&#40;"Simon", "Willison"&#41;
> s.fullName&#40;&#41;
Simon Willison
> s.fullNameReversed&#40;&#41;
Willison, Simon
Υπάρχει εδώ κάτι που συναντάμε για πρώτη φορά: το keyword this. Όταν χρησιμοποιείται μέσα σε μια συνάρτηση το this αναφέρεται στο τρέχον αντικείμενο. Το τι σημαίνει αυτό εξαρτάται από τον τρόπο που καλέσατε τη συνάρτηση. Αν την καλέσατε με χρήση της τελείας σε ένα αντικείμενο, αυτό το αντικείμενο γίνεται το this. Αν η κλήση έγινε χωρίς τη σύνταξη τελεία, το this αναφέρετε στο global object. Αυτή είναι μια συνηθισμένη πηγή σφαλμάτων. Για παράδειγμα:

Κώδικας: Επιλογή όλων

> s = makePerson&#40;"Simon", "Willison"&#41;
> var fullName = s.fullName;
> fullName&#40;&#41;
undefined undefined
Όταν καλέσαμε fullName() το this αναφέρθηκε στο global object. Καθώς δεν υπάρχουν global μεταβλητές first και last πήραμε undefined για την κάθε μια απ' αυτές.

Μπορούμε να εκμεταλλευτούμε το keyword this για να βελτιώσουμε τη συνάρτηση makePerson:

Κώδικας: Επιλογή όλων

function Person&#40;first, last&#41; &#123;
    this.first = first;
    this.last = last;
    this.fullName = function&#40;&#41; &#123;
        return this.first + ' ' + this.last;
    &#125;
    this.fullNameReversed = function&#40;&#41; &#123;
        return this.last + ', ' + this.first;
    &#125;
&#125;
var s = new Person&#40;"Simon", "Willison"&#41;;
Εγκαινιάσαμε άλλο ένα keyword: το new. Το new συνδέεται στενά με το this. Αυτό που κάνει είναι να δημιουργεί ένα ολοκαίνουργιο αντικείμενο, και μετά καλεί τη συνάρτηση και ταυτόχρονα κάνει το this να αναφέρεται σε αυτό το νέο αντικείμενο. Οι συναρτήσεις που σχεδιάστηκαν να καλούνται με το new ονομάζονται constructor functions. Είναι κοινή πρακτική να γράφουμε με κεφαλαίο πρώτο γράμμα αυτές τις συναρτήσεις, ως υπενθύμιση να τις καλέσουμε με το new.

Τα αντικείμενά μας γίνονται όσο πάει και καλύτερα, αλλά υπάρχουν ακόμα σε αυτά κάποιες άσχημες γωνίες. Κάθε φορά που δημιουργούμε ένα αντικείμενο person, δημιουργούμε ταυτόχρονα δύο ολοκαίνουργιες συναρτήσεις-αντικείμενα μέσα σε αυτό. Δε θα ήταν καλύτερο αν μπορούσαμε να κάνουμε κοινόχρηστο αυτόν τον κώδικα;

Κώδικας: Επιλογή όλων

function personFullName&#40;&#41; &#123;
    return this.first + ' ' + this.last;
&#125;
function personFullNameReversed&#40;&#41; &#123;
    return this.last + ', ' + this.first;
&#125;
function Person&#40;first, last&#41; &#123;
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
&#125;
Αυτό είναι καλύτερο: δημιουργούμε τις μεθόδους-συναρτήσεις μόνο μια φορά, και αποδίδουμε αναφορές σε αυτές μέσα στον constructor. Μπορούμε να κάνουμε τίποτα καλύτερο απ' αυτό; Η απάντηση είναι ναι:

Κώδικας: Επιλογή όλων

function Person&#40;first, last&#41; &#123;
    this.first = first;
    this.last = last;
&#125;
Person.prototype.fullName = function&#40;&#41; &#123;
    return this.first + ' ' + this.last;
&#125;
Person.prototype.fullNameReversed = function&#40;&#41; &#123;
    return this.last + ', ' + this.first;
&#125;
Το Person.prototype είναι ένα αντικείμενο που το μοιράζονται όλα τα αντικείμενα Person. Αποτελεί μέρος της αλυσίδας αναζήτησης ιδιοτήτων: κάθε φορά που επιχειρούμε να προσπελάσουμε μια ιδιότητα του Person που δεν έχει οριστεί, η JavaScript θα ελέγξει το Person.prototype για να δει αν αυτή η ιδιότητα μπορεί να βρεθεί εκεί. Σαν αποτέλεσμα οτιδήποτε προστίθεται στο Person.prototype γίνεται διαθέσιμο σε όλα τα αντικείμενα που κατασκευάστηκαν με αυτόν τον constructor.

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

Κώδικας: Επιλογή όλων

> s = new Person&#40;"Simon", "Willison"&#41;;
> s.firstNameCaps&#40;&#41;;
TypeError on line 1&#58; s.firstNameCaps is not a function
> Person.prototype.firstNameCaps = function&#40;&#41; &#123;
    return this.first.toUpperCase&#40;&#41;
&#125;
> s.firstNameCaps&#40;&#41;
SIMON
Είναι ενδιαφέρον ότι μπορείτε να προσθέσετε πράγματα στο prototype των ενσωματωμένων αντικειμένων της JavaScript. Ας προσθέσουμε μία μέθοδο στο String που να επιστρέφει αυτό το string αντεστραμμένο:

Κώδικας: Επιλογή όλων

> var s = "Simon";
> s.reversed&#40;&#41;
TypeError on line 1&#58; s.reversed is not a function
> String.prototype.reversed = function&#40;&#41; &#123;
    var r = '';
    for &#40;var i = this.length - 1; i >= 0; i--&#41; &#123;
        r += this&#91;i&#93;;
    &#125;
    return r;
&#125;
> s.reversed&#40;&#41;
nomiS
Η νέα μας μέθοδος δουλεύει ακόμα και σε string literals!

Κώδικας: Επιλογή όλων

> "This can now be reversed".reversed&#40;&#41;
desrever eb won nac sihT
Όπως ανέφερα προηγούμενα το prototype αποτελεί μέρος μιας αλυσίδας. Ο αρχικός κρίκος της αλυσίδας είναι το Object.prototype, του οποίου οι μέθοδοι περιλαμβάνουν το toString(). Αυτή είναι η μέθοδος που καλείται κάθε φορά που προσπαθείτε να μετατρέψετε ένα αντικείμενο σε string. Αυτό είναι χρήσιμο για το debugging του αντικειμένου μας:

Κώδικας: Επιλογή όλων

> var s = new Person&#40;"Simon", "Willison"&#41;;
> s
&#91;object Object&#93;
> Person.prototype.toString = function&#40;&#41; &#123;
    return '<Person&#58; ' + this.fullName&#40;&#41; + '>';
&#125;
> s
<Person&#58; Simon Willison>
Θυμάστε ότι η avg.apply() είχε ένα null ως πρώτο όρισμα; Μπορούμε τώρα να το αναθεωρήσουμε. Το πρώτο όρισμα στην apply() είναι το αντικείμενο που θα πρέπει να γίνει το this. Για παράδειγμα να μια επιπόλαιη υλοποίηση του new:

Κώδικας: Επιλογή όλων

function trivialNew&#40;constructor&#41; &#123;
    var o = &#123;&#125;; // Δημιουργία αντικειμένου
    constructor.apply&#40;o, arguments&#41;;
    return o;
&#125;
Αυτό δεν είναι ένα ακριβές υποκατάστατο του new γιατί παραλείπει τη σύνδεση με το κατάλληλο prototype. Είναι δύσκολο να βρεθούν καλά παραδείγματα για την επεξήγηση της apply(), και δεν είναι κάτι που χρησιμεύει πολύ συχνά, αλλά είναι χρήσιμο να ξέρετε γι αυτήν.

Η apply έχει μια αδελφή συνάρτηση με το όνομα call η οποία επίσης σας επιτρέπει να ορίζεται το this, αλλά αντί για ένα array δέχεται μια σειρά ορισμάτων.

Κώδικας: Επιλογή όλων

function lastNameCaps&#40;&#41; &#123;
    return this.last.toUpperCase&#40;&#41;;
&#125;
var s = new Person&#40;"Simon", "Willison"&#41;;
lastNameCaps.call&#40;s&#41;;
// Είναι το ίδιο όπως&#58;
s.lastNameCaps = lastNameCaps;
s.lastNameCaps&#40;&#41;;

Ένθετες Συναρτήσεις

Είναι επιτρεπτός ο ορισμός συναρτήσεων JavaScript μέσα σε άλλες συναρτήσεις. Το είδαμε προηγούμενα, σε μια συνάρτηση makePerson(). Μια σημαντική λεπτομέρεια των ένθετων συναρτήσεων είναι ότι έχουν πρόσβαση σε μεταβλητές στην εμβέλεια (scope) της εξωτερικής συνάρτησης:

Κώδικας: Επιλογή όλων

function betterExampleNeeded&#40;&#41; &#123;
    var a = 1;
    function oneMoreThanA&#40;&#41; &#123;
        return a + 1;
    &#125;
    return oneMoreThanA&#40;&#41;;
&#125;
Αυτό παρέχει ένα μεγάλο βαθμό ευκολίας στη συγγραφή περισσότερο συντηρήσιμου κώδικα. Αν μία συνάρτηση εξαρτάται από μία ή δύο άλλες συναρτήσεις οι οποίες δεν είμαι χρήσιμες σε άλλα μέρη του κώδικα, μπορείτε να ενθέσετε αυτές τις συναρτήσεις μέσα στη συνάρτηση που θα κληθεί από αλλού. Αυτό διατηρεί μειωμένο τον αριθμό των συναρτήσεων στην global scope, κάτι που είναι πάντα καλό πράγμα.

Αυτό είναι επίσης σημαντικό αντίμετρο στο δέλεαρ των global μεταβλητών. Όταν γράφουμε πολύπλοκο κώδικα είναι συχνά δελεαστικό να κάνουμε χρήση global μεταβλητών για να μοιραστούμε κοινές τιμές σε πολλές συναρτήσεις - το οποίο οδηγεί σε κώδικα που είναι δύσκολος στη συντήρηση. Οι ένθετες συναρτήσεις μπορούν να μοιραστούν μεταβλητές με τον γονέα τους, επομένως μπορείτε να χρησιμοποιήσετε αυτό το μηχανισμό για να ζευγαρώσετε συναρτήσεις όποτε έχει νόημα χωρίς να μολύνετε το global namespace - «local globals» αν προτιμάτε. Η χρήση αυτής της τεχνικής θα πρέπει να γίνεται με περίσκεψη, αλλά είναι μια χρήσιμη δυνατότητα να υπάρχει.


Closures

Αυτό μας οδηγεί σε μία από τις πιο ισχυρές αφαιρέσεις που έχει να προσφέρει η JavaScript - αλλά ενδεχομένως και την πιο δύσκολη να την κατανοήσει κανείς. Τι κάνει αυτό;

Κώδικας: Επιλογή όλων

function makeAdder&#40;a&#41; &#123;
    return function&#40;b&#41; &#123;
        return a + b;
    &#125;
&#125;
x = makeAdder&#40;5&#41;;
y = makeAdder&#40;20&#41;;
x&#40;6&#41;
?
y&#40;7&#41;
?
Το όνομα της συνάρτησης makeAdder θα πρέπει να δίνει την απάντηση: δημιουργεί νέες προσθετικές συναρτήσεις οι οποίες όταν καλούνται με ένα όρισμα το προσθέτουν στο όρισμα με το οποίο δημιουργήθηκαν.

Αυτό που συμβαίνει εδώ είναι πάνω-κάτω το ίδιο με αυτό που συνέβαινε με τις ένθετες συναρτήσεις νωρίτερα: μια συνάρτηση ορισμένη μέσα σε μια άλλη έχει πρόσβαση στις μεταβλητές τις εξωτερικής συνάρτησης. Η μόνη διαφορά εδώ είναι ότι η εξωτερική συνάρτηση έχει ολοκληρωθεί, επομένως η κοινή λογική φαίνεται να λέει ότι οι τοπικές μεταβλητές της δεν υπάρχουν πια. Αλλά αυτές εξακολουθούν να υπάρχουν, διαφορετικά οι συναρτήσεις adder δε θα ήταν σε θέση να λειτουργήσουν. Και όχι μόνο αυτό αλλά υπάρχουν δύο «αντίγραφα» των τοπικών μεταβλητών της makeAdder, ένα που είναι 5 και ένα που είναι 20.

Να τι συνέβη στην πραγματικότητα. Κάθε φορά που η JavaScript εκτελεί μία συνάρτηση δημιουργεί ένα αντικείμενο scope (εμβέλεια) για να κρατήσει τις τοπικές μεταβλητές που υπάρχουν μέσα στη ρουτίνα. Αρχικοποιείται με όσες μεταβλητές πέρασαν ως παράμετροι της συνάρτησης. Αυτό είναι παρόμοιο με το global object όπου ζουν όλες οι global μεταβλητές και συναρτήσεις, αλλά με δύο σημαντικές διαφορές: πρώτον δημιουργείται ένα ολοκαίνουργιο αντικείμενο scope κάθε φορά που ξεκινά η εκτέλεση μιας συνάρτησης, και δεύτερο, αντίθετα με το global object (που στους browsers είναι προσβάσιμο ως window) αυτά τα αντικείμενα scope δε μπορούν να τύχουν άμεσου χειρισμού από τον κώδικα JavaScript. Δεν υπάρχει για παράδειγμα μηχανισμός που να μας επιτρέπει να διατρέξουμε τις ιδιότητες του τρέχοντος αντικειμένου scope.

Έτσι όταν καλείται η makeAdder δημιουργείται ένα αντικείμενο scope με μία ιδιότητα: a, η οποία είναι το όρισμα που περάστηκε στη συνάρτηση makeAdder. Η makeAdder μετά επιστρέφει μια ολοκαίνουργια συνάρτηση. Κανονικά σε αυτό το σημείο ο garbage collector της JavaScript θα μάζευε το αντικείμενο scope που δημιουργήθηκε για την makeAdder, αλλά η συνάρτηση που επεστράφη διατηρεί μια αναφορά σε αυτό το αντικείμενο scope. Σαν αποτέλεσμα το αντικείμενο scope δεν πρόκειται να συλλεχθεί από τον garbage collector μέχρις ότου πάψουν να υπάρχουν αναφορές στη συνάρτηση-αντικείμενο που επέστρεψε η makeAdder.

Τα αντικείμενα scope σχηματίζουν μία αλυσίδα που ονομάζεται scope chain, παρόμοια με την αλυσίδα των prototypes που χρησιμοποιούνται από το σύστημα αντικειμένων της JavaScript.

Μία closure είναι ο συνδυασμός μίας συνάρτησης και του αντικειμένου scope στο οποίο δημιουργήθηκε.

Οι closures σας επιτρέπουν να αποθηκεύσετε δεδομένα κατάστασης (state), και ως τέτοιες μπορούν συχνά να χρησιμοποιηθούν στη θέση αντικειμένων.


Διαρροές μνήμης

Μια δυσάρεστη παρενέργεια των closures είναι ότι κάνουν ιδιαίτερα εύκολη τη διαρροή μνήμης (memory leak) στον Internet Explorer. Η JavaScript είναι μία γλώσσα garbage collected - τα αντικείμενα κατά τη δημιουργία τους καταλαμβάνουν χώρο στη μνήμη και αυτή η μνήμη ανακτάται από τον browser όταν δεν απομένουν άλλες αναφορές στο αντικείμενο. Τα αντικείμενα που παρέχει το περιβάλλον τα διαχειρίζεται το ίδιο το περιβάλλον.

Οι browsers χρειάζεται να διαχειριστούν ένα μεγάλο αριθμό από αντικείμενα που αντιπροσωπεύουν την εκάστοτε HTML σελίδα - τα αντικείμενα του DOM. Είναι ευθύνη του browser να διαχειριστεί τη δέσμευση και αποδέσμευση της μνήμης.

Ο Internet Explorer χρησιμοποιεί το δικό του σύστημα garbage collection γι αυτά τα DOM αντικείμενα, ξεχωριστά από το μηχανισμό που χρησιμοποιεί η JavaScript. Είναι η αλληλεπίδραση μεταξύ των δύο που μπορεί να γίνει αιτία για διαρροές μνήμης.

Μια διαρροή μνήμης συμβαίνει στον IE κάθε φορά που σχηματίζεται μία κυκλική αναφορά ανάμεσα σε ένα αντικείμενο JavaScript και ένα DOM αντικείμενο. Θεωρήστε το εξής:

Κώδικας: Επιλογή όλων

function leakMemory&#40;&#41; &#123;
    var el = document.getElementById&#40;'el'&#41;;
    var o = &#123; 'el'&#58; el &#125;;
    el.o = o;
&#125;
Η κυκλική αναφορά που σχηματίστηκε εδώ προκαλεί διαρροή μνήμης. Ο IE δε θα αποδεσμεύσει τη μνήμη που είναι σε χρήση για τα αντικείμενα el και o μέχρις ότου ο browser κάνει ολική επανεκκίνηση.

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

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

Οι closures κάνουν εύκολη την ακούσια δημιουργία διαρροών μνήμης. Δείτε αυτό:

Κώδικας: Επιλογή όλων

function addHandler&#40;&#41; &#123;
    var el = document.getElementById&#40;'el'&#41;;
    el.onclick = function&#40;&#41; &#123;
        this.style.backgroundColor = 'red';
    &#125;
&#125;
Ο παραπάνω κώδικας ορίζει το στοιχείο που με κλικ θα γίνει κόκκινο. Επίσης δημιουργεί διαρροή μνήμης. Γιατί; Διότι η αναφορά στο el έχει πιαστεί ακούσια από την closure που δημιουργήθηκε για την ανώνυμη εσωτερική συνάρτηση. Αυτό δημιουργεί μία κυκλική αναφορά ανάμεσα σε ένα αντικείμενο JavaScript (τη συνάρτηση) και ένα εγγενές DOM αντικείμενο (το el).

Υπάρχει μια σειρά από workarounds γι αυτό το πρόβλημα. Το απλούστερο είναι:

Κώδικας: Επιλογή όλων

function addHandler&#40;&#41; &#123;
    var el = document.getElementById&#40;'el'&#41;;
    el.onclick = function&#40;&#41; &#123;
        this.style.backgroundColor = 'red';
    &#125;
    el = null;
&#125;
Αυτό λειτουργεί με το να σπάει την κυκλική αναφορά.

Αναπάντεχα, ένα τρικ για το σπάσιμο μιας κυκλικής αναφοράς που προκλήθηκε από μια closure είναι η προσθήκη μίας ακόμα closure:

Κώδικας: Επιλογή όλων

function addHandler&#40;&#41; &#123;
    var clickHandler = function&#40;&#41; &#123;
        this.style.backgroundColor = 'red';
    &#125;
    &#40;function&#40;&#41; &#123;
        var el = document.getElementById&#40;'el'&#41;;
        el.onclick = clickHandler;
    &#125;&#41;&#40;&#41;;
&#125;
Η εσωτερική συνάρτηση εκτελείται άμεσα, και κρύβει τα περιεχόμενά της από την closure που δημιουργήθηκε με την clickHandler.

Ένα άλλο καλό τρικ για την αποφυγή των closures είναι το σπάσιμο των κυκλικών αναφορών κατά το συμβάν window.onunload. Πολλές βιβλιοθήκες συμβάντων θα αναλάβουν να το κάνουν για λογαριασμό σας.
____________________________________________________________

Ο Simon Willison εργάζεται για την Yahoo!, και στο blog του κάνει συχνές αναφορές σε θέματα JavaScript. Ακόμα θα είναι ένας από τους ομιλητές του συνεδρίου @media2006. Το παραπάνω κείμενο μεταφράστηκε χωρίς την άδειά του, αλλά υποθέτω ότι δε θα έχει αντίρρηση. :)
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Άβαταρ μέλους
ThyClub
Honorary Member
Δημοσιεύσεις: 5312
Εγγραφή: 17 Νοέμ 2003 00:21
Τοποθεσία: Hell's Kitchen
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από ThyClub » 13 Μαρ 2006 09:32

omg!!!

Άβαταρ μέλους
softius
Script Master
Δημοσιεύσεις: 241
Εγγραφή: 11 Ιαν 2004 19:07
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από softius » 14 Μαρ 2006 07:26

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

Άβαταρ μέλους
cherouvim
Script Master
Δημοσιεύσεις: 3137
Εγγραφή: 13 Ιούλ 2005 22:56
Τοποθεσία: Athens, Greece
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από cherouvim » 14 Μαρ 2006 08:38

Sygharitiria gia ti metafrasi kai gia to epikairo yliko pou panta prosfereis.

Άβαταρ μέλους
alkisg
Δημοσιεύσεις: 265
Εγγραφή: 03 Ιουν 2005 11:53
Τοποθεσία: Ιωάννινα
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από alkisg » 14 Μαρ 2006 08:42

:kaloe: :kaloe: :kaloe:

Άβαταρ μέλους
gre85
Δημοσιεύσεις: 11
Εγγραφή: 14 Απρ 2005 14:46
Τοποθεσία: πειραιας

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από gre85 » 16 Μαρ 2006 15:05

πολυ καλο... :wink:

Άβαταρ μέλους
cherouvim
Script Master
Δημοσιεύσεις: 3137
Εγγραφή: 13 Ιούλ 2005 22:56
Τοποθεσία: Athens, Greece
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από cherouvim » 16 Μαρ 2006 16:20

Ante na erthei kai i 15h Iouniou... /http://www.vivabit.com/atmedia2006/sessions/#dom :)

Άβαταρ μέλους
Basilakis
PHP Moderator
Δημοσιεύσεις: 8574
Εγγραφή: 17 Νοέμ 2003 13:03
Τοποθεσία: Womans' Brain
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από Basilakis » 17 Μαρ 2006 14:17

Kataplhktiko skeftomilos... Congats... :)

Άβαταρ μέλους
melodos
Δημοσιεύσεις: 4
Εγγραφή: 07 Φεβ 2007 19:54
Τοποθεσία: Σάμη, Κεφαλονιά
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από melodos » 30 Μαρ 2007 21:16

Πολλές ευχαριστίες για την μετάφραση...
ξανά ευχαριστώ!!!
Ρωμανός ο Μελωδός

Άβαταρ μέλους
dimsis
Reporter
Δημοσιεύσεις: 7994
Εγγραφή: 25 Ιούλ 2001 03:00

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από dimsis » 31 Μαρ 2007 09:10

skeftomilos καταρχήν grand :respect:
2ον πως θα γίνει να μαζευτούν όλα τα code tutorials σε κανένα site αποκλειστικά για coders να τα έχουμε προσβάσιμα εύκολα και απλά;
Γιάννη (cordis) τι λες;
Κεντρικές κατηγορίες από α) web design β) desktop programming γ) web programming δ) databases κλπ και υποκατηγορίες Flash, PHP, Javascript, MySQL, SQL Server κλπ κλπ και μέσα τα καλύτερα tutorials από εδώ (αλλά και από οποιονδήποτε άλλο ενδιαφερθεί);
Έχει μερικά Ελληνικά sites που το παλεύουν να κάνουν κάτι τέτοιο, αλλά όλο και κάπου πάσχουν είτε από interface, είτε από οργάνωση, είτε από δημοσιότητα, είτε από ευκολία κλπ κλπ

Το είχα κάνει παλιά στο code.gr (σαν εσωτερικό sub-site) και είχε τους φανατικούς οπαδούς του. Δεν ήταν πολλοί σε αριθμό, αλλά όσοι ασχολούνται με το "άθλημα" το είχαν εκτιμήσει και στέλνανε υλικό... όπως είχα γράψει και εγώ διάφορα δικά μου. Όλα ακόμα υπάρχουν (λογικά) σε κάποιο backup...

Άβαταρ μέλους
EkLekTos
WebDev Moderator
Δημοσιεύσεις: 7421
Εγγραφή: 07 Απρ 2005 15:44
Τοποθεσία: Inside the Effects
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από EkLekTos » 01 Απρ 2007 00:54

uparxei idi omws http://www.freestuff.gr/tutorials/ giati na ginei allo.. ;)
Gia mena den krinetai kapoio tutorial kalo i kako alla kala grammeno kai kaka grammeno, kai to paramikro bothima pou mporei na ginei kapoion se kapoion tha xreiastei ;) den paei na pei pws oloi tha to diabasoun, eksalou ta tutorials ginetai gia autous pou ta xreiazontai kai oxi gia olous ;);) . Elipizw na katalabes ti ennow :P
* Apple Technical Support Specialist *
* Apple Sales & Product Professional Certificate since 2011 * Εικόνα
Follow me @Twitter

Άβαταρ μέλους
skeftomilos
Script Master
Δημοσιεύσεις: 2888
Εγγραφή: 07 Ιαν 2005 07:22
Τοποθεσία: Αθήνα

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από skeftomilos » 01 Απρ 2007 12:50

Μια πρόσφατη φωτογραφία του Simon Willison, από συνέντευξη στο Vitamin

Εικόνα
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Άβαταρ μέλους
dimsis
Reporter
Δημοσιεύσεις: 7994
Εγγραφή: 25 Ιούλ 2001 03:00

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από dimsis » 01 Απρ 2007 13:29

EkLekTos έγραψε:uparxei idi omws http://www.freestuff.gr/tutorials/ giati na ginei allo.. ;)
Gia mena den krinetai kapoio tutorial kalo i kako alla kala grammeno kai kaka grammeno, kai to paramikro bothima pou mporei na ginei kapoion se kapoion tha xreiastei ;) den paei na pei pws oloi tha to diabasoun, eksalou ta tutorials ginetai gia autous pou ta xreiazontai kai oxi gia olous ;);) . Elipizw na katalabes ti ennow :P
Με το σκεπτικό σου θα πρέπει να μπουν στα tutorials και όλες οι απαντήσεις που έχουμε δώσει π.χ. με κώδικα σε όσους έχουν ζητήσει βοήθεια στο forum που είναι εκατοντάδες. Θα 'ταν καλό πάντως αυτά τα tutorials (αλλά και άλλα) που έχει και στην αντίστοιχη ενότητα εδώ ο Γιάννης, να τα βρίσκαμε και σε ένα ξεχωριστό domain αποκλειστικά για αυτό το σκοπό. Αν έχει ο Γιάννης κανένα ελεύθερο domain και την διάθεση για κάτι τέτοιο, θα μπορούσε να το κάνει.

Άβαταρ μέλους
geoki
Δημοσιεύσεις: 309
Εγγραφή: 07 Ιαν 2002 01:00
Τοποθεσία: Giannitsa

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από geoki » 07 Απρ 2007 05:03

Ευχαριστούμε skeftomilos φοβερή δουλειά

Άβαταρ μέλους
fusion
Δημοσιεύσεις: 146
Εγγραφή: 23 Αύγ 2008 21:34
Τοποθεσία: Λάρισα
Επικοινωνία:

Εισαγωγή στη σύγχρονη JavaScript

Δημοσίευση από fusion » 21 Σεπ 2008 16:06

Εχω ξεκινησει να το διαβαζω, πολυ ενδιαφερον...
Μαγκας ο skeftomilos ;)

Απάντηση

Επιστροφή στο “JavaScript και Frameworks”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 1 επισκέπτης