JavaScript για προχωρημένους: Objects

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

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

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

JavaScript για προχωρημένους: Objects

Δημοσίευση από skeftomilos » 02 Αύγ 2005 23:59

JavaScript Objects

Ένα tutorial για αντικείμενα μπορεί να φαίνεται κοινότυπο στις μέρες μας που ο αντικειμενοστραφής προγραμματισμός είναι τόσο διαδεδομένος, και οι σχετικές πηγές τόσο άφθονες. Ωστόσο κι αν ακόμα γνωρίζετε τα πάντα για τα αντικείμενα στη Java ή τη VB.NET, η προσέγγιση της JavaScript έχει ορισμένες ιδιαιτερότητες που θα σας προκαλέσουν έκπληξη ή αμηχανία. Βλέπετε ο χαρακτήρας της JavaScript δεν είναι αυστηρός και άκαμπτος όπως στις παραπάνω γλώσσες, αλλά αντίθετα διακρίνεται για την πλαστικότητα και την ευελιξία του. Κύριος σκοπός μου σε αυτό το άρθρο είναι να διαφημίσω αυτό το δυναμικό στοιχείο της γλώσσας, χωρίς αυτό να σημαίνει ότι αυτό το στυλ προγραμματισμού είναι υποχρεωτικό. Η JavaScript επιτρέπει να προγραμματίζουμε με την ίδια επιτυχία και με τα τρία βασικά στυλ, procedural, object-oriented και functional, και μπορούμε να διαλέξουμε όποιο ταιριάζει με τις γνώσεις και τα γούστα μας. Αλλά καλύτερα ν' αφήσουμε τη θεωρία και να περάσουμε αμέσως στην πράξη. Ας αρχίσουμε να φτιάχνουμε μερικά αντικείμενα!

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

var o = {}
var o = new Object()
Αυτοί είναι οι δύο πιο συνηθισμένοι τρόποι δημιουργίας αντικείμενων στη JavaScript. Με τον πρώτο τρόπο χρησιμοποιούμε object initializers που χαρακτηρίζονται από τις αγκύλες {}. Με το δεύτερο τρόπο χρησιμοποιούμε constructor functions και τον τελεστή new. Πρέπει να πούμε ότι στη JavaScript δεν υπάρχουν κλάσεις. Τα αντικείμενα δεν είναι κουλουράκια που βγαίνουν όλα ομοιόμορφα από ένα καλούπι (κλάση), αλλά το καθένα έχει τη δική του προσωπικότητα. Υπάρχει ωστόσο το concept του prototype, με το οποίο θα ασχοληθούμε παρακάτω. Συγχωρέστε με που θ' αφήσω αμετάφραστη τη λέξη prototype, στο κάτω-κάτω ελληνική λέξη είναι! :)

Object Initializers

Οι object initializers είναι ο ποιο συμπαγής τρόπος για να φτιάξουμε αντικείμενα. Ας δούμε μερικά παραδείγματα:

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

var book = {}                               // Αντικείμενο χωρίς ιδιότητες.
var book = {title: 'La Peste'}              // Αντικείμενο με μία ιδιότητα title.
var book = {title: 'La Peste', year: 1947}  // Αντικείμενο με δύο ιδιότητες title και year.
Βλέπετε ότι ανάμεσα στις αγκύλες τοποθετούμε ζεύγη ιδιότητα:τιμή που χωρίζονται με κόμμα. Δηλαδή αποδίδουμε τιμές στις ιδιότητες κατά τον ορισμό τους, καθώς στη JavaScript δεν έχει και πολύ νόημα μία ιδιότητα χωρίς τιμή. Μετά τον ορισμό του αντικειμένου, μπορούμε να διαβάσουμε τις ιδιότητές του ως εξής:

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

var book = {title: 'La Peste', year: 1947}
alert(book.title)
Εικόνα

Ως τιμές των ιδιοτήτων μπορούμε να δώσουμε strings, αριθμούς, boolean τιμές, arrays ή και άλλα αντικείμενα. Π.χ.

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

var book = {title: 'La Peste', year: 1947, stock: true}

// title => string
// year  => αριθμός
// stock => boolean

var author = {name: 'Albert Camus', books:['La Peste', 'L`etranger'], birthdate: new Date(1913, 10, 7)}

// name      => string
// books     => array με δύο στοιχεία
// birthdate => Date object
Εκτός από ιδιότητες, ένα αντικείμενο μπορεί να έχει και μεθόδους. Μεθόδους λέμε τις ιδιότητες εκείνες που περιέχουν functions. Αυτές οι ρουτίνες συνήθως χρησιμοποιούνται για να κάνουν διάφορους υπολογισμούς με τις υπόλοιπες ιδιότητες του αντικειμένου, η πρόσβαση στις οποίες γίνεται με τη χρήση του ειδικού keyword this. Παράδειγμα:

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

var book = {title: 'La Peste', year: 1947, toString: function() { return this.title + " (" + this.year + ")" } }
Χμ, σαν πολύ να μάκρυνε η εντολή! Ας εκμεταλλευτούμε τη συντακτική ευελιξία της JavaScript στο χωρισμό των γραμμών για να την κάνουμε πιο ευανάγνωστη:

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

var book = {
  title: 'La Peste',
  year: 1947,
  toString: function() { return this.title + " (" + this.year + ")" }
}
Καλύτερα, δε συμφωνείτε; Χρειάζεται μόνο προσοχή σε ένα συνηθισμένο συντακτικό σφάλμα, όταν ξεχνάμε να αφαιρέσουμε το κόμμα από την τελευταία ιδιότητα ή μέθοδο. Να το έχετε υπόψη σας γιατί θα σας βασανίσει συχνά. Όμως η σύνταξη μπορεί να βελτιωθεί ακόμα περισσότερο:

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

var book = {
  title: 'La Peste',
  year: 1947,

  toString: function() {
    return this.title + " (" + this.year + ")"
  }
}
Τώρα μάλιστα! :) Είναι πια φανερό τι κάνει η μέθοδος toString. Επιστρέφει τα περιεχόμενα του αντικειμένου με τη μορφή ενός καλοσχηματισμένου string. Ας τη δοκιμάσουμε στην πράξη για να δούμε πώς λειτουργεί:

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

alert(book.toString())
Εικόνα

Βλέπετε πώς χρησιμοποιούμε το keyword this μέσα στο σώμα της ρουτίνας. Το this αναφέρετε στο τρέχον αντικείμενο, αυτό του οποίου η μέθοδος εκτελείται. Θα μπορούσατε να πείτε ότι στο συγκεκριμένο παράδειγμα η χρήση του this είναι περιττή, και ότι θα μπορούσε να αντικατασταθεί με τη μεταβλητή book:

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

toString: function() {
  return book.title + " (" + book.year + ")"
}
Έχετε δίκιο, αλλά γενικά η χρήση του this παρέχει απείρως περισσότερη ευελιξία.

Να σημειώσουμε εδώ ότι το όνομα toString που δώσαμε στη μέθοδό μας έχει μία ειδική σημασία. Είναι η μέθοδος που καλεί αυτόματα η JavaScript όταν της ζητήσουμε να μετατρέψει σε string ένα αντικείμενο. Για να γίνει αυτό κατανοητό ας δοκιμάσουμε την παρακάτω εντολή:

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

alert(book)
Η ρουτίνα alert() περιμένει να της δώσουμε ένα string για να το εμφανίσει ως μήνυμα στο χρήστη. Αν δεν της δώσουμε string αλλά κάτι άλλο, θα προσπαθήσει να το μετατρέψει σε string με τον καλύτερο τρόπο που μπορεί. Συγκεκριμένα θα ψάξει να βρει τη μέθοδο toString του αντικειμένου και αν υπάρχει θα τη χρησιμοποιήσει. Έτσι στην περίπτωσή μας θα δούμε αυτό το μήνυμα:

Εικόνα

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

Εικόνα

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

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

alert(Object.prototype.toString())
...αλλά για τα prototypes θα μιλήσουμε παρακάτω. Ας μην ξεχνάμε ότι η JavaScript είναι case-sensitive γλώσσα, επομένως είναι άλλο toString και άλλo tostring και ToString.

Αφήνοντας προσωρινά τους object initializers να πούμε ότι είναι γνωστοί και με μία άλλη ορολογία: literal notation. Αν διαβάσετε πουθενά για object literals να ξέρετε ότι αφορούν αυτή την τεχνική δημιουργίας αντικειμένων.

Constructor Functions

Αυτός ο τρόπος δημιουργίας αντικειμένων είναι πιο γνώριμος για όσους γνωρίζουν από αντικείμενα σε άλλες γλώσσες, και πιο κατάλληλος από τους object initializers όταν χρειαζόμαστε πολλά ομοειδή αντικείμενα. Η JavaScript διαθέτει μερικές ενσωματωμένες τέτοιες constructor functions, όπως Array(), Date() και RexExp():

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

var d = new Date(2006, 0, 15)               // ημερομηνία 15 Ιανουαρίου 2006
var a = new Array('La Peste', 'L`etranger') // array με δύο στοιχεία
var re = new RegExp('.*\\.jpg$')            // pattern αναζήτησης εικόνων JPEG
Βέβαια εδώ μας ενδιαφέρει να μάθουμε πώς να φτιάχνουμε τις δικές μας constructor functions, όχι να χρησιμοποιούμε τις υπάρχουσες. Βασικά δε διαφέρουν ιδιαίτερα από τις κοινές functions. Καμιά φορά μάλιστα είναι δυνατό να τις καλέσουμε και χωρίς το new μπροστά. Και οι τρεις παραπάνω εντολές θα ήταν εντελώς ισοδύναμες αν αφαιρούσαμε το new, αλλά η παρουσία του κάνει πιο κατανοητή τη λογική του κώδικα. Η καλύτερη ένδειξη ότι μία function χρησιμοποιείται ως constructor είναι το αρχικό κεφαλαίο γράμμα. Πρόκειται για σύμβαση ονοματολογίας που θα πρέπει να ακολουθείτε πιστά. Ας δούμε λοιπόν τώρα μία τέτοια function:

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

function Book(title, year) {
  this.title = title
  this.year = year
}

var book = new Book('La Peste', 1947)

alert(book.title)
Εικόνα

Το αντικείμενο book που κατασκευάσαμε μόλις τώρα είναι ισοδύναμο με το...

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

var book = {title: 'La Peste', year: 1947}
...που είχαμε φτιάξει πρωτύτερα. Για την ακρίβεια είναι σχεδόν και όχι απόλυτα ισοδύναμο γιατί διαφέρουν ως προς το prototype. Αλλά ας αναβάλλουμε τη σχετική συζήτηση για αργότερα. Αυτό που κερδίσαμε φτιάχνοντας αυτή τη ρουτίνα, είναι η δυνατότητα να τη χρησιμοποιήσουμε πολλές φορές για να φτιάξουμε περισσότερα αντικείμενα:

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

var book1 = new Book('The Clan of the Cave Bear', 1980)
var book2 = new Book('The Valley of Horses', 1985)
var book3 = new Book('The Mammoth Hunters', 1988)
var book4 = new Book('The Plains of Passage', 1990)
Θα προσπαθήσω να εξηγήσω τι ακριβώς κάνει το keyword new μπροστά από την κλήση της ρουτίνας Book(). Κατ' αρχήν δημιουργεί ένα νέο αντικείμενο, το οποίο περνά ως τιμή του keyword this. Γίνεται δηλαδή κάτι σαν...

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

this = {}
...αν και στην πραγματικότητα αυτή η σύνταξη δεν είναι έγκυρη (δε μπορούμε εμείς να αποδώσουμε οτιδήποτε στο this). Μέσα στο σώμα της ρουτίνας προστίθενται δύο ιδιότητες στο νέο αντικείμενο, η title και η year. Οι τιμές τους προέρχονται από τα arguments της ρουτίνας. Τέλος η συνάρτηση επιστρέφει το νέο αντικείμενο, παρόλο που η εντολή...

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

return this
...απουσιάζει από το σώμα της ρουτίνας. Βλέπουμε λοιπόν ότι το new αλλάζει κάπως τη συμπεριφορά της ρουτίνας. Άραγε τη θα συνέβαινε αν την καλούσαμε χωρίς το new;

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

var book = Book('La Peste', 1947)
Τώρα δε δημιουργείται κανένα νέο αντικείμενο. Η Book() δεν επιστρέφει τίποτα απολύτως, επομένως η book θα διατηρήσει την τιμή undefined. Ωστόσο οι εντολές μέσα στο σώμα της ρουτίνας θα εκτελεστούν.

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

this.title = title
this.year = year
Το ερώτημα που προκύπτει έχει να κάνει με το this. Σε τι αναφέρεται τώρα; Δεν υπάρχει νέο αντικείμενο όπως είπαμε, επομένως δεν υπάρχει κάτι συγκεκριμένο. Ελλείψει άλλου καλύτερου υποψηφίου το this αναφέρεται στο Global Object, που στην περίπτωση της client-side JavaScript είναι το window! Με λίγα λόγια αυτό που καταφέραμε είναι να προσθέσουμε τις άχρηστες ιδιότητες title και year στο αντικείμενο window, όπως μπορούμε να δούμε εκτελώντας την παρακάτω εντολή:

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

alert(window.title)
Εικόνα

Όλα αυτά τα πειράματα είναι χρήσιμα ελπίζω για να καταλάβουμε σε βάθος τη φύση των keywords new και this.

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

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

function Book(title, year) {
  this.title = title
  this.year = year
  this.toString = function() {
    return this.title + " (" + this.year + ")"
  }
}

var book = new Book('L`etranger', 1942)

alert(book)
Εικόνα

Αλλαγή τιμών ιδιοτήτων

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

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

var runner = {name: 'Kenteris', speed: 35}
alert(runner.speed) // Εμφανίζει μήνυμα 35
runner.speed = 40
alert(runner.speed) // Εμφανίζει μήνυμα 40
runner.speed += 2
alert(runner.speed) // Εμφανίζει μήνυμα 42
runner.speed++
alert(runner.speed) // Εμφανίζει μήνυμα 43
Μάλιστα εκτός από την τιμή είναι δυνατό να αλλάξουμε και τον τύπο τις ιδιότητας:

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

runner.speed = 'rocket'
alert(runner.speed) // Εμφανίζει μήνυμα 'rocket'
...αλλά καλό είναι να αποφεύγουμε τέτοια ακροβατικά, εκτός αν το μεταμεσονύκτιο debugging είναι κάτι που μας αρέσει! :) Η JavaScript πάντως δε μας εμποδίζει να αλλάζουμε τις τιμές των ιδιοτήτων με όποιο τρόπο θέλουμε. Ένας τρόπος να περιορίσουμε κάπως αυτή την επικίνδυνη ευελιξία είναι να προσθέτουμε στα αντικείμενα ειδικές ρουτίνες απόδοσης τιμής (setters) με ονόματα όπως setSpeed(). Δε θα επεκταθώ όμως περισσότερο σε αυτές τις τεχνικές γιατί είμαι της γνώμης ότι δεν είναι ταιριαστές με το πνεύμα αυτής της γλώσσας. Είναι ωστόσο πολύ ταιριαστές με το πνεύμα της Java, αλλά γι αυτήν αρμόδιος να μιλήσει είναι ο Doctor, όχι εγώ (btw Doctor χρωστάς ένα tutorial ή κάνω λάθος; :D)

Επέκταση αντικειμένων

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

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

var book = {title: 'La Peste', year: 1947}
book.author = 'Albert Camus'
book.toString = function() {
  return this.title + ", "  + this.author + " (" + this.year + ")"
}

alert(book)
Εικόνα

Αυτό εννοούσα προηγουμένως λέγοντας ότι τα αντικείμενα έχουν χωριστή προσωπικότητα. Μπορεί να κατασκευάσαμε δύο αντικείμενα book1 και book2 με τη βοήθεια της constructor function Book(), αλλά αυτό δε σημαίνει κατ' ανάγκη ότι τα αντικείμενα μοιράζονται και τις ίδιες ιδιότητες. Βέβαια προκύπτουν κάποια ερωτήματα: Πώς ξέρουμε αν ένα αντικείμενο διαθέτει ή όχι μία ιδιότητα; Τι θα συμβεί αν προσπαθήσουμε να διαβάσουμε μία ιδιότητα που δεν υπάρχει; Ας αναζητήσουμε την απάντηση στο δεύτερο ερώτημα πρώτα:

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

alert(book.price)
Εικόνα

Καμιά ζημιά δε συνέβη. Μάλιστα μας δίνει και την απάντηση για το πρώτο ερώτημα. Για να ελέγξουμε αν το αντικείμενο book διαθέτει την ιδιότητα price, αρκεί να τη συγκρίνουμε με την τιμή undefined:

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

if (book.price !== undefined) {
  alert('exists!')
} else {
  alert('not exists!')
}
Ωστόσο αυτή η προσέγγιση δεν είναι απόλυτα ορθή. Είναι δυνατό να υπάρχει η ιδιότητα book.price αλλά να έχει τιμή undefined. Στην πράξη αυτό ισοδυναμεί με το να μην υπάρχει καθόλου, αλλά αν συναντήσουμε κάποια περίπτωση που αυτό να έχει σημασία, υπάρχει καλύτερη λύση:

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

if ('price' in book) {
  alert('exists!')
} else {
  alert('not exists!')
}
Ο τελεστής in πρωτοεμφανίστηκε στη JavaScript 1.4 με συνέπεια ο παραπάνω κώδικας να "χτυπήσει" στον Netscape 4, κάτι που προσωπικά δε με απασχολεί καθόλου. :)

Αφού είδαμε τον τρόπο να επεκτείνουμε ένα αντικείμενο, ας δούμε και τον τρόπο για να το συρρικνώσουμε!

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

delete book.year
alert('year' in book) // Εμφανίζει μήνυμα false
Αυτό αφαιρεί την ιδιότητα year από το αντικείμενο book. Προσωπικά ουδέποτε χρειάστηκε να χρησιμοποιήσω στην πράξη τον τελεστή delete.

Prototypes

Όλα τα αντικείμενα συνδέονται με κάποιο prototype. Τα prototypes δεν είναι παρά κοινά, συνηθισμένα αντικείμενα, τα οποία όμως λειτουργούν ως κοινή παρακαταθήκη ιδιοτήτων για άλλα αντικείμενα. Ας δούμε για παράδειγμα τι συνέβη προηγουμένως όταν ζητήσαμε την τιμή της ιδιότητας book.price. Η JavaScript έψαξε τις ιδιότητες του book για κάποια που να λέγετε price, αλλά δε βρήκε καμία. Δεν εγκατέλειψε όμως εδώ την προσπάθεια. Συνέχισε την αναζήτηση στο prototype του book. Ούτε εδώ όμως βρήκε τίποτα, επομένως επέστρεψε την τιμή undefined. Τι θα είχε συμβεί αν η price είχε βρεθεί ως ιδιότητα του prototype του book; Η JavaScript θα είχε επιστρέψει κανονικότατα την τιμή αυτής της ιδιότητας, σα να μη συνέβαινε τίποτα. Με λίγα λόγια τα prototypes αποτελούν επεκτάσεις των αντικειμένων με τα οποία συνδέονται. Πάντως όλα τα prototypes αρχικά είναι κενά, και αποκτούν ιδιότητες μόνο από δικές μας ενέργειες. Επομένως μπορούμε να τα αγνοήσουμε αν κρίνουμε ότι δε μας χρειάζονται.

Πώς συνδέεται όμως ένα αντικείμενο με το prototype του; Η σύνδεση γίνεται αυτόματα όταν δημιουργούμε ένα αντικείμενο:

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

var book = new Book('L`etranger', 1942)
Το prototype του book είναι το Book.prototype.

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

var obj = new Object()
Το prototype του obj είναι το Object.prototype.

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

var book = {title: 'L`etranger', year: 1942}
Το prototype του book είναι το Object.prototype!

Βλέπουμε ότι τα prototypes των αντικειμένων είναι αποθηκευμένα σε μία κατάλληλη ιδιότητα της constructor function που τα δημιούργησε. Χμ, ναι, δεν ξέρω αν σας το είπα αλλά οι functions είναι αντικείμενα! Βασικά τα πάντα είναι αντικείμενα στη JavaScript, οπότε μη σας προκαλεί εντύπωση. :) Προσέξτε μόνο ότι τα αντικείμενα που κατασκευάζονται με object initializer λαμβάνουν ως prototype το Object.prototype. Αυτό σημαίνει ότι αυτές οι δύο εντολές είναι απόλυτα ισοδύναμες:

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

var o = {}
var o = new Object()
Ας δούμε τώρα ένα πρακτικό παράδειγμα χρήσης των prototypes:

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

function Book(title, year) {
  this.title = title
  this.year = year
}

Book.prototype.toString = function() {
  return this.title + " (" + this.year + ")"
}

var book = new Book('La Peste', 1947)

alert(book)
Εικόνα

Εδώ αντί να προσθέσουμε τη μέθοδο toString() στο αντικείμενο book μέσα από το σώμα της ρουτίνας Book(), προτιμήσαμε να την προσθέσουμε στο Book.prototype. Στην πράξη δεν έχει καμιά διαφορά απολύτως. Από τεχνική άποψη η αλλαγή αυτή έβλαψε ελαφρά την ταχύτητα εκτέλεσης του κώδικα αλλά μείωσε τις απαιτήσεις σε RAM. Πρόκειται πάντως για εντελώς αμελητέες ποσότητες, εκτός αν έχουμε να κάνουμε με χιλιάδες αντικείμενα οπότε θ' αρχίσουν να μας απασχολούν και τέτοια ζητήματα.

Τώρα που είδαμε τι είναι πάνω-κάτω τα prototypes πρέπει να κάνω μια διόρθωση. Η αναζήτηση μίας ιδιότητας δε σταματά στο prototype ενός αντικειμένου αλλά συνεχίζει στο prototype του prototype! Αυτό συνεχίζεται ξανά και ξανά, μέχρι να βρεθεί κάποιο prototype χωρίς prototype. Αυτό συνήθως είναι το Object.prototype για το οποίο ισχύει:

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

('prototype' in Object.prototype) == false
Μπερδευτήκατε; Είναι μπέρδεμα σίγουρα, πάντως αυτός ο μηχανισμός επιτρέπει την ύπαρξη κληρονομικότητας στη JavaScript. Μην περιμένετε να μάθετε από μένα τίποτα για κληρονομικότητα, καθώς θεωρώ ότι δε συμβαδίζει με το πνεύμα της γλώσσας. Όσοι πάντως έχετε σχετικές ανησυχίες θα βρείτε μερικά links στο τέλος για να χορτάσετε την αρρωστημένη σας περιέργεια. :P Δε θα πω πολλά ακόμα για τα prototypes καθώς το θέμα είναι νομίζω υπερβολικά εξεζητημένο. Μόνο θα πω ότι είναι επιτρεπτή η αντικατάσταση του prototype μίας ρουτίνας με ένα άλλο:

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

Book.prototype = {stock: true}
...καθώς και η αποσύνδεση ενός αντικειμένου από το prototype του:

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

book.__proto__ = null
...ή η σύνδεσή του με ένα άλλο prototype:

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

book.__proto__ = Array.prototype
Όλα τα αντικείμενα διαθέτουν την κρυφή ιδιότητα __proto__, η οποία τα συνδέει με το prototype τους.

Τα αντικείμενα ως arrays

Τα αντικείμενα στη JavaScript είναι arrays! Δεν έχουν όλα τα χαρακτηριστικά των arrays (π.χ. ιδιότητα length) αλλά επιτρέπουν την πρόσβαση στις ιδιότητές τους με την ίδια σύνταξη:

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

var runner = {name: 'Kenteris', speed: 35}
alert(runner['speed']) // Εμφανίζει μήνυμα 35
runner['speed'] = 40
alert(runner['speed']) // Εμφανίζει μήνυμα 40
Το πρακτικό όφελος είναι ότι μπορούμε να δώσουμε στις ιδιότητες ονόματα που δεν επιτρέπονται σε άλλες γλώσσες:

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

runner['what a name!'] = 0
Αυτή η δυνατότητα υπάρχει και στη literal notation:

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

var runner = {name: 'Kenteris', speed: 35, 'what a name!': 0}
typeof, instanceof

Με τον τελεστή typeof παίρνουμε μια περιγραφή του τύπου ενός αντικειμένου, συνήθως όμως ανεπαρκή:

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

var book = new Book('La Peste', 1947)
alert(typeof book)
Εικόνα

Με τον τελεστή instanceof μαθαίνουμε αν το αντικείμενο κατασκευάστηκε από μία συγκεκριμένη constructor function ή όχι:

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

var book = new Book('La Peste', 1947)
alert(book instanceof Book) // Εμφανίζει μήνυμα true
for ... in

Η JavaScript προσφέρει τον κομψό βρόγχο for ... in για της απαρίθμηση των ιδιοτήτων και μεθόδων ενός αντικειμένου:

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

var book = {title: 'La Peste', year: 1947}
for (var p in book) {
  alert(p + " = " + book[p])
}
Θα εμφανίσει με τη σειρά τα δύο παρακάτω μηνύματα:

Εικόνα

Εικόνα

Επέκταση υπαρχόντων αντικειμένων

Έχω βρει χρήσιμο κατά καιρούς να προσθέτω μεθόδους σε υπάρχοντα αντικείμενα μέσω του prototype τους. Π.χ. σε αυτό το topic υπάρχει μία επέκταση για τα αντικείμενα Number:
Number.prototype.toZeroPaddedString
Number.prototype.toTimeString
...ενώ σε αυτό υπάρχει μία επέκταση για τα Date:
Date.prototype.toStringGreek
Υπάρχει πάντως μία σύσταση για αποφυγή κάθε επέκτασης του Object.prototype, γιατί μπορεί να προκαλέσει προβλήματα σε scripts που βασίζονται στον βρόγχο for ... in. Το Object.prototype είναι κοινή παρακαταθήκη για όλα μα όλα τα αντικείμενα (δικά μας και ξένα), οπότε καλύτερα να μην το πειράζουμε.

Άλλες φορές πάλι έχω προσθέσει ιδιότητες σε DOM elements κατευθείαν (όχι στο prototype). Δηλαδή κάπως έτσι:

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

var div = document.getElementById("message")
div.message = "An error occured"
Αυτά τα ολίγα για σήμερα, συν μερικά links για περαιτέρω ανάγνωση. :P

- A Survey of the JavaScript Programming Language
- Classical Inheritance in JavaScript
- Rethinking JavaScript Objects
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Cmg__
Δημοσιεύσεις: 1710
Εγγραφή: 29 Μαρ 2005 22:40

JavaScript για προχωρημένους: Objects

Δημοσίευση από Cmg__ » 05 Αύγ 2005 01:36

:clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap:

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

JavaScript για προχωρημένους: Objects

Δημοσίευση από skeftomilos » 05 Αύγ 2005 05:18

Έλεγα κι εγώ ... πού είναι ο Σιμιτζούλης, πού χάθηκε αυτή η ψυχή; :D :lol:
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Άβαταρ μέλους
cordis
Administrator, [F|H]ounder, [C|S]EO
Δημοσιεύσεις: 27622
Εγγραφή: 09 Οκτ 1999 03:00
Τοποθεσία: Greece
Επικοινωνία:

JavaScript για προχωρημένους: Objects

Δημοσίευση από cordis » 05 Αύγ 2005 22:03

όπως πάντα άκρως περιγραφικός ;)
Δεν απαντάω σε προσωπικά μηνύματα με ερωτήσεις που καλύπτονται από τις ενότητες του forum. Για ο,τι άλλο είμαι εδώ για εσάς.
- follow me @twitter

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

JavaScript για προχωρημένους: Objects

Δημοσίευση από skeftomilos » 06 Αύγ 2005 06:52

Πρόσφατα προσέθεσα και τον IE5 στους browsers για δοκιμή σελίδων (τον κατέβασα από εδώ). Αυτή η παλιά έκδοση συνόδευε αν δεν κάνω λάθος τα Win98 First Edition, οπότε λογικά δεν πρέπει να έχει σημαντικό μερίδιο σήμερα. Όμως δε νομίζω ότι μπορούμε ακόμα να τον αγνοήσουμε (του χρόνου ίσως :)) γιατί έστω κι ένα ποσοστό 3% είναι σημαντικό.

Ο IE5 δεν είναι ιδιαίτερα διαφορετικός από τον IE6, αλλά ορισμένες μέθοδοι απουσιάζουν από κάποια built-in αντικείμενα όπως:
Array.push
Array.pop
Array.shift
Array.unshift
Array.splice

Function.call
Function.apply
Workarounds υπάρχουν εδώ. Ωστόσο πριν λίγο εντόπισα μία πολύ ουσιώδη διαφορά. Ο IE5 Δεν επιτρέπει στις εκφράσεις το keyword undefined. Επομένως οι παρακάτω εκφράσεις "χτυπάνε":

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

var a = undefined
if (a == undefined) {}
Τα παρακάτω είναι έγκυρα υποκατάστατα:

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

var a
if (a == null) {}
if (typeof a == 'undefined') {}
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

Cmg__
Δημοσιεύσεις: 1710
Εγγραφή: 29 Μαρ 2005 22:40

JavaScript για προχωρημένους: Objects

Δημοσίευση από Cmg__ » 07 Αύγ 2005 00:10

περιμένουμε κι αλλα!!!!

panosru
WebDev Moderator
Δημοσιεύσεις: 1885
Εγγραφή: 13 Σεπ 2005 16:13
Τοποθεσία: Camp

JavaScript για προχωρημένους: Objects

Δημοσίευση από panosru » 08 Φεβ 2006 21:18

tora to eida ayto!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
skeftomilos an den yphrxes isos akoma na misousa thn javascript! oso diabazo ta tutorial sou me kanoun na latrebo thn javascript!!!!!!!

pantos einai xaotikh glossa

PS: agorasa to javascript bible 5th edition apo papasothriou! (1236 selides :roll: )

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

JavaScript για προχωρημένους: Objects

Δημοσίευση από skeftomilos » 09 Φεβ 2006 04:03

Καλό κουράγιο! Ό,τι απορίες προκύψουν εδώ είμαστε. :)
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

panosru
WebDev Moderator
Δημοσιεύσεις: 1885
Εγγραφή: 13 Σεπ 2005 16:13
Τοποθεσία: Camp

JavaScript για προχωρημένους: Objects

Δημοσίευση από panosru » 10 Φεβ 2006 16:28

kamia aporia mou fanhkan poly katanohta! kai fysika xarhs thn akros leptomeriakh sou perigrafh!!! :D

ps: o kathigitis mou sto iek pou mas kanei javascript trabaei ta malia tou tora pou ta ta grafo ola se objects giati tou bganei h pisth na ta diabasei! lool

eimai kakos lool :yea: :yea: :kaloe:

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

JavaScript για προχωρημένους: Objects

Δημοσίευση από skeftomilos » 11 Φεβ 2006 19:48

Τότε γράψε και σε functional στυλ να τραβάει και τα μουστάκια του! :strong:

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

var towns = {
  list: 'Paris;London;Budapest'.split(';'),
  iterate: function(f) {
    for &#40;var i = 0; i < this.list.length; i++&#41; f&#40;this.list&#91;i&#93;&#41;
  &#125;
&#125;
towns.iterate&#40;function&#40;town&#41; &#123;
  alert&#40;town&#41;
&#125;&#41;
The pure and simple truth is rarely pure and never simple. Ο μη νους δε σκέπτεται μη σκέψεις για το τίποτα.

panosru
WebDev Moderator
Δημοσιεύσεις: 1885
Εγγραφή: 13 Σεπ 2005 16:13
Τοποθεσία: Camp

JavaScript για προχωρημένους: Objects

Δημοσίευση από panosru » 11 Φεβ 2006 20:54

koita! hparxh kai h ekdoxh anti na trabaei ta moustakia tou na me kopsei sto eksamhno! xaxaxa (einai kai to 4o kai to teleytaio krima eima! :P )

Απάντηση

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

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

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