2013-02-06 10 views
7

Abbiamo discusso sul modo migliore per gestire gli oggetti nella nostra app JS, studiando il libro di Stoyan Stefanov, leggendo infiniti post SO su "nuovi", "questo", "prototipo", chiusure ecc. (Il fatto che ce ne sono così tanti, e hanno così tante teorie in competizione, suggerisce che non c'è una risposta completamente ovvia).Cosa c'è di sbagliato in questo stile di codifica JavaScript? (chiusure vs prototipi)

Quindi supponiamo che non ci interessi i dati privati ​​. Ci accontentiamo di affidare a utenti e sviluppatori il compito di non gironzolare sugli oggetti al di fuori dei modi che definiamo.

Dato questo, quale (a parte quello che sembra sfidare decenni di stile OO e cronologia) sarebbe sbagliato con questa tecnica?

// namespace to isolate all PERSON's logic 
var PERSON = {}; 

// return an object which should only ever contain data. 
// The Catch: it's 100% public 

PERSON.constructor = function (name) { 
    return { 
     name: name 
    } 
} 

// methods that operate on a Person 
// the thing we're operating on gets passed in 

PERSON.sayHello = function (person) { 
    alert (person.name); 
} 

var p = PERSON.constructor ("Fred"); 
var q = PERSON.constructor ("Me"); 

// normally this coded like 'p.sayHello()' 
PERSON.sayHello(p); 
PERSON.sayHello(q); 

Ovviamente:

  1. Non ci sarebbe niente da impedire a qualcuno di mutare 'p' in empie modi, o semplicemente la logica della PERSONA finire la diffusione in tutto il luogo. (Questo vale anche per la "nuova" tecnica canonica).
  2. Sarebbe un problema minore passare "p" in ogni funzione che si desiderava utilizzare .
  3. Questo è un approccio strano .

Ma sono sufficienti motivi per respingerlo? Sul lato positivo:

  1. Si è efficiente, come (probabilmente) opposta chiusure con dichiarazione di funzione ripetitiva.
  2. Sembra molto semplice e comprensibile, al contrario di giocherellare con 'questo' ovunque.

Il punto chiave è precedente privacy. So che mi sbatterò per questo, ma, alla ricerca di qualsiasi feedback. Saluti.

+0

C'è un punto nel costruttore di una persona con questo approccio? basta usare il dattilografo 'PERSON.sayHello ({nome:" Fred "})' – house9

+0

i miei approcci preferiti con js - 1) usare il coffeescript, non una cura tutto ma dà l'illusione delle classi OO - 2) Modello costruttore funzionale da " JavaScript: The Good Parts "pg 52 - http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742 – house9

+0

@ house9 ma un toccasana per cosa? Le classi sono solo sintassi e sintassi che hanno più senso in un paradigma precompilato. Perché la gente si blocca così quando cerca di imparare JS? La chiave per comprendere JS è capire le funzioni di JS. Dopo che i costruttori sono un gioco da ragazzi. Non c'è nulla di rotto nell'approccio di JS a OOP. Ci vuole un bel po 'meno codice per applicare la maggior parte dei pattern orientati all'OOP in JS rispetto ai linguaggi precompilati rispetto ai quali è in genere confrontato. –

risposta

8

Non c'è niente di intrinsecamente sbagliato in questo. Ma rinuncia a molti vantaggi inerenti all'uso del sistema prototipo di Javascript.

  • L'oggetto non sa nulla di se stesso se non che è un oggetto letterale. Quindi, instanceof non ti aiuterà a identificare la sua origine. Sarai bloccato usando solo la digitazione delle anatre.
  • I metodi sono essenzialmente funzioni statiche con nomi dei nomi, in cui è necessario ripetere se stessi passando l'oggetto come primo argomento. Avendo un oggetto prototipato, è possibile sfruttare la spedizione dinamica , in modo che p.sayHello() possa fare cose diverse per PERSON o ANIMAL a seconda del tipo Javascript lo sa. Questa è una forma del polimorfismo . Il tuo approccio richiede di nominare (e possibilmente commettere un errore) il tipo ogni volta che chiami un metodo.
  • In realtà non è necessaria la funzione constructor, poiché le funzioni sono già oggetti.La tua variabile PERSON potrebbe anche essere la funzione di costruzione.

Quello che abbiamo fatto qui è creare un modulo modello (come uno spazio dei nomi).

Ecco un altro modello che mantiene quello che hai, ma fornisce i vantaggi di cui sopra:

function Person(name) 
{ 
    var p = Object.create(Person.prototype); 
    p.name = name; // or other means of initialization, use of overloaded arguments, etc. 
    return p; 
} 
Person.prototype.sayHello = function() { alert (this.name); } 

var p = Person("Fred"); // you can omit "new" 
var q = Person("Me"); 
p.sayHello(); 
q.sayHello(); 
console.log(p instanceof Person); // true 
var people = ["Bob", "Will", "Mary", "Alandra"].map(Person); 
     // people contains array of Person objects 
+0

Mi scuso per non aver accettato prima, grazie mille per le risposte eccellenti. Saluti! – GregT

1

Sì, io non sono realmente capire il motivo per cui si sta cercando di schivare l'approccio costruttore o perché si sono sentiti anche un Bisogna sovrapporre i costruttori di zucchero sintattico su funzione (Object.create e presto classi) quando i costruttori da soli sono un approccio elegante, flessibile e perfettamente ragionevole all'OOP, non importa quante ragioni siano giustificate da persone come Crockford per non averli graditi (perché le persone dimenticano di usare seriamente la nuova parola chiave?). JS è fortemente guidato dalle funzioni e la sua meccanica OOP non è diversa. È meglio abbracciarlo piuttosto che nasconderlo, IMO.

Prima di tutto, i punti elencati sotto "Ovviamente"

  1. difficilmente anche la pena menzionare in JavaScript. Gli alti gradi di mutabilità sono designati. Non abbiamo paura di noi stessi o di altri sviluppatori in JavaScript. Il paradigma privato contro pubblico non è utile perché ci protegge dalla stupidità, ma piuttosto perché rende più facile comprendere l'intenzione dietro il codice dell'altro dev.

  2. Lo sforzo di invocare non è il problema. La seccatura arriva più tardi quando non è chiaro il motivo per cui hai fatto ciò che hai fatto lì. Non vedo veramente quello che stai cercando di ottenere che gli approcci linguistici fondamentali non facciano al caso tuo.

  3. Questo è JavaScript. È stato strano per tutti tranne gli sviluppatori di JS per anni. Non preoccuparti se trovi un modo migliore di fare qualcosa che funzioni meglio nella risoluzione di un problema in un dato dominio rispetto a una soluzione più tipica. Assicurati di aver compreso il punto dell'approccio più tipico prima di provare a sostituirlo come molti hanno avuto quando si è presentato a JS da altri paradigmi linguistici. È facile fare cose banali con JS ma una volta che sei nel punto in cui vuoi ottenere più OOP-driven, impara tutto ciò che puoi su come funziona la lingua di base, così puoi applicare un po 'più di scetticismo alle opinioni popolari diffuse da persone che fanno una vita di lato facendo scattare JavaScript per essere più spaventoso e più crivellato di trappole mortali esplosive di quello che realmente è.

Ora i vostri punti sotto "lato positivo",

  1. Prima di tutto, definizione di funzione ripetitiva era davvero solo qualcosa di cui preoccuparsi nello scenario looping pesante. Se producessi regolarmente oggetti con una quantità sufficientemente grande abbastanza veloce da rendere le definizioni dei metodi pubblici non prototipati un problema, probabilmente ti imbatterai in problemi di utilizzo della memoria con oggetti non banali in breve tempo. Parlo al passato, tuttavia, perché in entrambi i casi non è più una questione rilevante. Nei browser moderni, le funzioni definite all'interno di altre funzioni in genere sono in genere il miglioramento delle prestazioni a causa del modo in cui funzionano i moderni compilatori JIT. Indipendentemente dai browser supportati, alcune funzioni definite per oggetto non sono un problema a meno che non ti aspetti decine di migliaia di oggetti.

  2. Sulla questione del semplice e comprensibile, non è per me perché non vedo che vittoria hai ottenuto qui.Ora invece di avere un oggetto da usare, devo usare insieme sia l'oggetto che il pseudo-costruttore che se non stavo guardando la definizione implicherebbe la funzione che usi con una "nuova" parola chiave per costruire oggetti . Se fossi nuovo nella tua base di codice, avrei perso un sacco di tempo cercando di capire perché l'avessi fatto in questo modo per evitare di rompere qualche altra preoccupazione che non comprendevo.

Le mie domande sarebbero:

Perché non aggiungere tutti i metodi nell'oggetto letterale nel costruttore, in primo luogo? Non ci sono problemi di prestazioni là e non c'è mai stato così l'unica altra vittoria possibile è che vuoi essere in grado di aggiungere nuovi metodi alla persona dopo aver creato nuovi oggetti con esso, ma questo è ciò che usiamo per prototipi su costruttori appropriati (I metodi di prototipazione sono ottimi per la memoria nei browser più vecchi perché sono definiti una sola volta).

E se devi continuare a passare l'oggetto per i metodi per sapere quali sono le proprietà, perché vuoi anche oggetti? Perché non solo le funzioni che si aspettano oggetti di tipo struttura dati semplici con determinate proprietà? Non è più OOP più.

Ma il mio principale punto di critica

Ti manca il punto principale della programmazione orientata agli oggetti che è qualcosa JavaScript fa un lavoro migliore di non nascondere da parte di persone che maggior parte delle lingue. Si consideri il seguente:

function Person(name){ 
    //var name = name; //<--this might be more clear but it would be redundant 
    this.identifySelf = function(){ alert(name); } 
} 

var bob = new Person(); 
bob.identifySelf(); 

Ora, modificare il nome del bob si identifica con, senza sovrascrivere l'oggetto o il metodo, che sono due le cose che faresti solo se fosse chiaro che non voleva lavorare con la oggetto come originariamente progettato e costruito. Ovviamente non puoi. Ciò rende perfettamente chiaro a chiunque veda questa definizione che il nome è effettivamente una costante in questo caso. In un costruttore più complesso stabilirebbe che l'unica cosa che può modificare o modificare il nome è l'istanza stessa, a meno che l'utente non abbia aggiunto un metodo setter non validante che sarebbe sciocco perché sarebbe fondamentalmente (osservando i Java Enterprise Beans) SCOPO CENTRALE DI OOP.

chiara divisione di responsabilità è la chiave

dimenticare le parole chiave che hanno messo in ogni libro per un secondo e pensare a cosa il punto è. Prima di OOP, tutto era solo una pila di funzioni e strutture dati su cui agivano tutte quelle funzioni. Con OOP si dispone principalmente di un insieme di metodi in bundle con un set di dati che solo l'oggetto stesso cambia effettivamente.

Quindi diciamo che qualcosa è andato storto con uscita:

  • Nel nostro mucchio strettamente procedurale delle funzioni non c'è nessun vero limite al numero di mani che avrebbero potuto incasinato tali dati. Potremmo avere una buona gestione degli errori, ma una funzione potrebbe ramificarsi in modo tale che il colpevole originale sia difficile da rintracciare.

  • In un design OOP corretto in cui i dati sono in genere dietro un gatekeeper di oggetti, so che solo un oggetto può effettivamente rendere le modifiche responsabili.

oggetti di esporre tutti i loro dati il ​​più delle volte è davvero solo marginalmente migliore rispetto al vecchio approccio procedurale.Tutto ciò che realmente fa è darti un nome per categorizzare i metodi liberamente correlati con.

Molto rumore per 'questo'

non ho mai capito l'attenzione indebita assegnata al 'questo' parola chiave essere disordinato e confuso. Non è davvero un grosso problema. 'this' identifica l'istanza con cui stai lavorando. Questo è tutto. Se il metodo non viene chiamato come proprietà, non saprà quale istanza cercare, per cui l'impostazione predefinita è l'oggetto globale. È stato stupido (undefined sarebbe stato meglio), ma non funziona correttamente in quello scenario dovrebbe essere previsto in un linguaggio in cui le funzioni sono anche portatili come i dati e possono essere facilmente collegate ad altri oggetti. Utilizzare 'this' in una funzione quando:

  • È definito e definito come proprietà di un'istanza.

  • Viene passato come gestore di eventi (che verrà chiamato come membro della cosa che viene ascoltata).

  • Stai utilizzando i metodi call o apply per chiamarlo temporaneamente come proprietà di qualche altro oggetto senza assegnarlo come tale.

Ma ricorda, è la chiamata che conta davvero. Assegnare un metodo pubblico ad alcune var e chiamare da quella var farà la cosa globale o genererà un errore in modalità strict. Senza essere referenziati come proprietà dell'oggetto, le funzioni interessano solo l'ambito in cui sono state definite (le loro chiusure) e il modo in cui le si supera.

+0

Grazie mille per l'eccellente risposta. Saluti – GregT

Problemi correlati