Microsoft's Internet Explorer browser has no built-in vector graphics machinery required for "loss-free" gradient background themes.
Please upgrade to a better browser such as Firefox, Opera, Safari or others with built-in vector graphics machinery and much more. (Learn more or post questions or comments at the Slide Show (S9) project site. Thanks!)
Prototype-based programming is a style of object-oriented programming in which classes are not present, and behavior reuse (known as inheritance in class-based languages) is performed via a process of cloning existing objects that serve as prototypes. This model can also be known as class-less, prototype-oriented or instance-based programming.
The original (and most canonical) example of a prototype-based language is the programming language Self developed by David Ungar and Randall Smith. However, the classless programming style has recently grown increasingly popular, and has been adopted for the programming languages JavaScript, Cecil, NewtonScript, Io, MOO, REBOL, Lisaac and several others.
From Wikipedia
In prototype-based languages that use delegation, the language runtime is capable of dispatching the correct method or finding the right piece of data simply by following a series of delegation pointers (from object to its prototype) until a match is found. All that is required to establish this behavior-sharing between objects is the delegation pointer. Unlike the relationship between class and instance in class-based object-oriented languages, the relationship between the prototype and its offshoots does not require that the child object have a memory or structural similarity to the prototype beyond this link. As such, the child object can continue to be modified and amended over time without rearranging the structure of its associated prototype as in class-based systems.
From Wikipedia
Under pure prototyping, which is also referred to as concatenative prototypes, and is exemplified in the Kevo language, there are no visible pointers or links to the original prototype from which an object is cloned. The prototype object is copied exactly, but given a different name (or reference). Behavior and attributes are simply duplicated as-is. Advantages to this approach include the fact that object authors can alter the copy without worrying about side-effects across other children of the parent. A further advantage is that the computational cost of method lookup during dispatch is drastically reduced when compared to delegation, where an exhaustive search must be made of the entire delegation chain before failure to find a method or slot can be admitted.
From Wikipedia
Quali sono gli obiettivi che ci prefiggiamo di ottenere mediante l’OOP?
In Javascript è possibile combinare lo stato e il comportamento in un oggetto senza passare necessariamente dal concetto di classe:
var car = { color: "red", model: "Fiat Uno", state: "off", max_velocity: 140, turnOn: function() { var msg = { "on": " is already on.", "off": " is turned on." }; if(this.state === "on") { print("Your "+this.model+msg["on"]+"!!!"); } else { this.state = "on" print("Now Your "+this.model+msg["off"]); } } }
Analogamente ai linguaggi OOP noti (C++ e Java ad esempio) nei metodi di un oggetto si può far riferimento all’oggetto stesso mediante la parola chiave this . A differenza di C++ e Java pero’ un metodo può essere decontestualizzato ( unbind ) e agganciato ad un altro contesto ( bind ):
var another_car = { color: "green", model: "Fiat 500", state: "off" }; var turnOn = car.turnOn; turnOn.call(another_car); turnOn.apply(another_car);
Per agganciare un metodo ad un oggetto in modo “definitivo” è sufficiente assegnarlo ad una proprietà dell’oggetto e richiamarlo mediante la sintassi “puntata”:
another_car.myTurnOnMethod = car.turnOn;
another_car.myTurnOnMethod();
ATTENZIONE pero’:
richiamando il metodo di un oggetto di cui si è fatto unbind senza l’uso dei metodi call o apply implica l’errato binding di this all’oggetto corrente… che disgraziatamente potrebbe essere l’oggetto globale :-(
turnOn.call(another_car); // <--------- esegue turnOn con **this = another_car** turnOn(); // <--------- esegue turnOn con **this = global** // (di solito l'oggetto window)
Cosa succede se vogliamo creare diversi oggetti simili tra loro per proprietà e comportamento?
Nella programmazione OOP Class-based l’approccio naturale è quello di creare una classe da cui creare gli oggetti…
In Javascript si crea una funzione (detta funzione costruttore) e gli si applica l’operatore new il quale alloca un nuovo oggetto che la funzione costruttore si occuperà di inizializzare:
function Car(model,color) { this.model = model || "Fiat Uno"; this.color = color || "red"; this.state = "off"; } a_car = new Car(); another_car = new Car("Fiat 500","green");
E’ molto IMPORTANTE non dimenticare l’operatore new davanti ad un costruttore!!!
In caso contrario this risulterebbe ancora una volta erroneamente collegato all’oggetto globale, la funzione costruttore spesso ritornerà undefined e ad essere inizializzato non sarebbe il nuovo oggetto creato da new ma l’oggetto globale.
Per questo motivo per convenzione le funzioni costruttore hanno nomi in CamelCase
your_car = Car(); your_car // undefined model // "Fiat Uno" color // "red" state // "off"
Anche se, come tutti i linguaggi dinamici ed interpretati, in Javascript i meccanismi di garbage collection si occupano di liberare per noi la memoria dagli oggetti non più utilizzati ( referenziati ), mediante l’operatore delete possiamo dargli una mano, oltre a poter eliminare singole proprietà degli oggetti:
ref2a_car = a_car delete a_car.color; // rimuove la proprietà color dall'oggetto a_car a_car.color; // La proprietà risulta rimossa da entrambi gli ref2a_car.color; // identificativi delete a_car; // elimina il riferimento all'oggetto a_car ref2a_car; // <--------- l'oggetto esiste ancora
L’operatore delete ritorna true quando è possibile eliminare un riferimento ad un oggetto, quando ciò non è possibile? Quando un identificativo è stato creato con var:
other_car = {}; var a_car = {}; delete other_car; // true delete a_car; // false
Car.prototype.turnOn = function() { var msg = { "on": " is already on.", "off": " is turned on." }; if(this.state === "on") { print("Your "+this.model+msg["on"]+"!!!"); } else { this.state = "on" print("Now Your "+this.model+msg["off"]); } }; Car.prototype.describe = function() { print("This is a "+this.color+" "+this.model); } a_car = new Car("Fiat 500","green"); a_car.describe();
la proprietà proto è una proprietà nascosta di ogni object, in firefox e webkit è accessibile in lettura/scrittura ma non è enumerabile.
function Y10() {} Y10.prototype = new Car(); mycar = new Y10(); mycar instanceof Y10; // true mycar instanceof Car; // true mycar.turnOn // function <--- ereditata da Car
La prototype chain viene consultata bottom-up solo in fase di lookup, mentre le assegnazioni agiscono solo sull’object e non sui suoi prototype.
function Y10(color) { var super_turnOn = this.turnOn; this.color = color; this.turnOn = function() { if(Math.random()>0.6) super_turnOn() else print("cCCCCcccc cccCCCCcccc"); } } Y10.prototype = new Car("Y10"); Y10.model; Y10.color; Y10.turnOn(); <--------------- richiama il nuovo metodo
Abbiamo già visto come Javascript sia dotato di capacità di reflection attraverso gli operatori typeof e instanceof .
Essendo l’Object e l’Hash Table sinomini in Javascript, ed essendo l’oggetto il costrutto base ed onnipresente di Javascript, attraverso la struttura di controllo for…in è possibile enumerare (e quindi navigare) il contenuto della stragrande maggioranza dell’ambiente.
js> for (var i in a_car) { print(i); } model color state turnOn describe js>
L’operatore in può essere utilizzato al di fuori della struttura di controllo for…in allo scopo di effettuare un test booleano di presenza di una proprietà:
"model" in a_car // true "color" in a_car // true "state" in a_car // true "turnOn" in a_car // true "nonexistent" in a_car // false
Il metodo Object.isPrototypeOf (ereditato quindi da tutti gli oggetti) effettua un test booleano per verificare se due oggetti sono collegati tra di loro da una relazione di prototipazione:
function Car(); function Y10(); Y10.prototype = new Car(); a_car = new Y10(); Y10.prototype.isPrototypeOf(a_car); // true Car.prototype.isPrototypeOf(a_car); // true Object.prototype.isPrototypeOf(a_car); // true Date.prototype.isPrototypeOf(a_car); // false
I meccanismi di reflection visti sino ad ora (typeof, for…in, in, isPrototypeOf) sono tutti dei metodi di lookup ed in quanto tali seguono la prototype chain alla ricerca delle informazioni richieste sulle proprietà.
Per assicurarsi che una proprietà appartenga realmente ad un determinato oggetto e non alla sua prototype chain, è sufficiente far uso del metodo Object.hasOwnProperty
function Parent() {}; Parent.prototype.attribute_from_parent = "test" obj = new Parent; obj.attribute_from_obj = "test obj"; var msg; for (var i in obj) { msg = obj.hasOwnProperty(i) ? "possiede" : "ha ereditato"; print("L'oggetto "+msg+" la proprieta' "+i+"."); }
Le proprietà degli oggetti built-in ed alcune proprietà nascoste (ad esempio Function.prototype.constructor) non sono (intenzionalmente) enumerabili.
Come si è verificato precedentemente le proprietà di un oggetto in qualunque momento possono essere rimosse, aggiunte e modificate, possiamo ricavarci uno spazio privato/privilegiato a cui si possa accedere solo in modo controllato?
CERTO ci sono le closure :-D
function Car(model,color) { var model = typeof model === "string" ? model : "Fiat Uno"; var color = typeof color === "string" ? color : "red"; var state = "off"; this.get_model = function() { return model; }; this.get_color = function() { return color; }; this.set_color = function(value) { color = typeof value === "string" ? value : color; }; this.turnOn = function() { var msg = { "on": " is already on.", "off": " is turned on." }; if(state === "on") { print("Your "+model+msg["on"]+"!!!"); } else { state = "on" print("Now Your "+model+msg["off"]); } }; }
ATTENZIONE: questo approccio interagisce con difficoltà con il meccanismo della delegation attraverso la prototype chain. Lo stato privato della closure sarà condiviso dalle due istanze in quando contenuto nel loro prototipo comune.
function Y10() {} Y10.prototype = new Car("Y10"); a_car = new Y10(); other_car = new Y10(); a_car.get_color(); other_car.get_color(); a_car.set_color("black"); other_car.get_color();
Inoltre lo scope privato limita le nostre possibilità in termini di reflection.
Le tecniche di metaprogrammazione di Javascript sono basate soprattutto sulle closure e sulla possibilità di modificare a runtime gli oggetti e le funzioni costruttore.
La funzione eval costituisce un’altra allettante alternativa, ma la mancanza di reali meccanismi di sandboxing e il degrado di performance che la caratterizzano la rendono di fatto non utilizzabile realisticamente.
Crockford dimostra come è possibile prototipare degli oggetti da altri oggetti senza passare esplicitamente dalla definizione di funzioni costruttore:
Object.beget = function (o) { var F = function () {}; F.prototype = o; return new F(); };
car = { model: "Fiat Uno", color: "red", state: "off", turnOn: function() { var msg = { "on": " is already on.", "off": " is turned on." }; if(this.state == "on") { print("Your "+this.model+msg["on"]+"!!!"); } else { this.state = "on" print("Now Your "+this.model+msg["off"]); } } } ferrari = Object.beget(car); ferrari.model = "Ferrari" ferrari.turnOn();
Altro simpatico javascript design pattern descritto da Crockford è costituito dall’aggiunta di un metodo method al prototipo dell’oggetto Function al fine di poter aggiungere metodi al prototipo relativo ad una funzione costruttore in modo meno alieno
Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; Number.method('toInteger', function () { return Math[this < 0 ? 'ceiling' : 'floor'](this); }); (10 / 3).toInteger()
E per concludere, l’ultimo design pattern di Crockford consiste nell’aggiungere un metodo che renda più leggibile l’associazione tra due funzioni costruttore (che rappresentano in qualche modo dei tipi ) in una prototype chain:
Function.method('inherits', function (Parent) { this.prototype = new Parent(); return this; }); var Car = function() {} Ferrari = function() {}.inherits(Car);
E’ da notare come nei precedenti pattern di Crockford (method e inherits) i metodi ritornino this dopo aver modificato l’oggetto corrente ( this ). Questa semplice accortezza consente un altro pattern molto comune in molti framework javascript (e ruby) chiamato cascading
var Car = function(model, color) { this.model = model; this.color = color; }. method('turnOn', function() { print("this is the turning on method"); }); var Ferrari = function(color) { this.model = "Ferrari"; this.color = color || "red"; }. inherits(Car). method('describe', function() { print("this is the describe method"); });
Function.prototype.new = function new() { for each (var i in [Date, String, Number, Boolean, RegExp]) { if(this === i) return new this(arguments[0]); } var obj = new this; var result = this.apply(obj,arguments); return obj; } Number.new(15); RegExp.new(/^java.*$/); a_car = Car.new("maggiolino"); a_car instanceof Car; Number.new(10) instanceof Number; Date.new(2008,10,11) != new Date(2008,10,11) // <-------- dovuto all'implementazione // interna di Date
function User(first, last){ if ( !(this instanceof arguments.callee) ) return new User(first, last); this.name = first + " " + last; }