Introducció
JavaScript és un llenguatge basat en prototips i cada objecte en ell té una propietat interna oculta anomenada ]
que pot usar-se per estendre propietats i mètodes d’objectes. Podeu obtenir més informació sobre els prototips del nostre tutorial Informació sobre els prototips i l’herència en JavaScript.
Fins fa poc, els desenvolupadors esforçats usaven funcions constructores per imitar un patró de disseny orientat a objectes en JavaScript. L’especificació de llenguatge ECMAScript 2015, sovint anomenada ES6, va introduir classes en el llenguatge JavaScript. Les classes de JavaScript no ofereixen realment una funcionalitat addicional i sovint es descriuen com a elements que aporten “sucre sintàctic” en comparació amb els prototips i l’herència, ja que ofereixen una sintaxi més neta i elegant. A causa de que altres llenguatges de programació fan servir classes, la sintaxi de classe de JavaScript permet que els desenvolupadors alternin llenguatges de forma més directa.
Les classes són funcions
Una classe de JavaScript és un tipus de funció. Les classes es declaren amb la paraula clau class
. Farem servir sintaxi d’expressió de funció per inicialitzar una sintaxi d’expressió de funció i classe per inicialitzar una classe.
// Initializing a function with a function expressionconst x = function() {}
// Initializing a class with a class expressionconst y = class {}
Podem accedir a ]
d’un objecte usant el mètode Object.getPrototypeOf()
. Farem servir això per provar la funció buida que vam crear.
Object.getPrototypeOf(x);
Outputƒ () { }
També podem usar aquest mètode en la classe que acabem de crear.
Object.getPrototypeOf(y);
Outputƒ () { }
El codi declarat amb function
i class
mostra una funció ]
. Amb prototips, qualsevol funció pot convertir-se en una instància de constructor mediane la paraula clau new
.
const x = function() {}// Initialize a constructor from a functionconst constructorFromFunction = new x();console.log(constructorFromFunction);
Outputx {}constructor: ƒ ()
Això s’aplica a classes també.
const y = class {}// Initialize a constructor from a classconst constructorFromClass = new y();console.log(constructorFromClass);
Outputy {}constructor: class
Aquests exemples de constructors de prototips, en cas contrari, estan buits. No obstant això, podem veure que a sota de la sintaxi tots dos mètodes aconsegueixen el mateix resultat final.
Definir una classe
En el tutorial de prototips i herència, confeccionem un exemple basat en la creació de personatges en un joc de rols basat en text. Continuarem amb aquest exemple aquí per actualitzar la sintaxi de funcions a classes.
Una funció constructora s’inicialitza amb diversos paràmetres que s’assignarien com propietats de this
, en al·lusió a la pròpia funció. La primera lletra de l’identificador seria majúscula per convenció.
// Initializing a constructor functionfunction Hero(name, level) { this.name = name; this.level = level;}
Quan traduïm això a la sintaxi de classe, que es mostra a continuació, veiem que la seva estructura és força similar.
// Initializing a class definitionclass Hero { constructor(name, level) { this.name = name; this.level = level; }}
Sabem que una funció constructora està pensada per a ser un esquema d’objecte per l’ús de majúscula a la primera lletra de l’inicialitzador (opcional) ia través de la familiaritat amb la sintaxi. La paraula clau class
comunica d’una manera més directa l’objectiu de la nostra funció.
L’única diferència en la sintaxi de la inicialització té a veure amb l’ús de la paraula clau class
en lloc de function
, i amb l’assignació de les propietats dins d’un mètode constructor()
.
Definir mètodes
La pràctica comuna amb funcions constructores consisteix a assignar mètodes directament a prototype
en lloc de fer-ho amb la initialización, com s’aprecia en el mètode greet()
a continuació.
function Hero(name, level) { this.name = name; this.level = level;}// Adding a method to the constructorHero.prototype.greet = function() { return `${this.name} says hello.`;}
Amb classes, aquesta sintaxi es simplifica i el mètode pot agregar directament a les classes. A l’usar l’abreviatura de definició de mètode introduïda en ES6, s’aconsegueix una concisió encara més gran a l’definir un mètode.
class Hero { constructor(name, level) { this.name = name; this.level = level; } // Adding a method to the constructor greet() { return `${this.name} says hello.`; }}
Observem aquestes propietats i mètodes en acció. Crearem una nova instància de Hero
usant la paraula clau new
i assignarem alguns valors.
const hero1 = new Hero('Varg', 1);
Si imprimim més informació sobre el nostre nou objecte amb console.log(hero1)
, podem apreciar més detalls sobre el que succeeix amb la inicialització de classe.
OutputHero {name: "Varg", level: 1}__proto__: ▶ constructor: class Hero ▶ greet: ƒ greet()
Podem veure en el resultat que les funcions constructor()
i greet()
es van aplicar a __proto__
, o ]
de hero1
, i no directament com a mètode en l’objecte hero1
. Encara que això queda de manifest en la creació de funcions de constructor, no resulta evident a l’crear classes. Les classes permeten una sintaxi més senzilla i succinta, encara que sacrifiquen una mica de claredat en el procés.
Ampliar una classe
Una característica avantatjosa de les funcions i classes constructores és que poden ampliar-se a nous esquemes d’objecte basats en l’element principal. Això impedeix la repetició de codi per objectes similars, però necessiten característiques addicionals o més específiques.
Es poden crear noves funcions constructores des de l’element principal usant el mètode call()
. En el següent exemple, crearem una classe de caràcter més específica anomenada Mage
i li assignarem les propietats de Hero
usant call()
, a més d’afegir una propietat.
// Creating a new constructor from the parentfunction Mage(name, level, spell) { // Chain constructor with call Hero.call(this, name, level); this.spell = spell;}
En aquest punt, podem crear una nova instància de Mage
usant les mateixes propietats de Hero
, així com també una nova que afegim.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
a l’enviar hero2
a la consola, podem apreciar que vam crear una nova classe Mage
basada en la constructora .
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}__proto__: ▶ constructor: ƒ Mage(name, level, spell)
Amb classes de ES6, la paraula clau super
s’usa en lloc de call
per accedir a les funcions principals. Farem servir extends
per fer referència a la classe principal.
// Creating a new class from the parentclass Mage extends Hero { constructor(name, level, spell) { // Chain constructor with super super(name, level); // Add a new property this.spell = spell; }}
Ara podem crear una nova instància de Mage
de la mateixa manera.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Imprimirem hero2
a la consola i veurem el resultat.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}__proto__: Hero ▶ constructor: class Mage
el resultat és gairebé exactament el mateix, amb la diferència que en la construcció de classe ]
es vincula a l’element principal; en aquest cas, Hero
.
A continuació, es mostra una comparació en paral·lel de tot el procés d’inicialització, incorporació de mètodes i herència d’una funció constructor i una classe.
function Hero(name, level) { this.name = name; this.level = level;}// Adding a method to the constructorHero.prototype.greet = function() { return `${this.name} says hello.`;}// Creating a new constructor from the parentfunction Mage(name, level, spell) { // Chain constructor with call Hero.call(this, name, level); this.spell = spell;}
// Initializing a classclass Hero { constructor(name, level) { this.name = name; this.level = level; } // Adding a method to the constructor greet() { return `${this.name} says hello.`; }}// Creating a new class from the parentclass Mage extends Hero { constructor(name, level, spell) { // Chain constructor with super super(name, level); // Add a new property this.spell = spell; }}
Tot i que la sintaxi és força diferent, el resultat subjacent és gairebé el mateix per a tots dos mètodes. Les classes ens ofereixen una alternativa més concisa per a crear esquemes d’objecte i les funcions constructor descriuen amb més precisió el que succeeix en segon pla.
Conclusió
En aquest tutorial, va incorporar coneixements sobre les similituds i diferències entre les funcions constructor de JavaScript i les classes de ES6. Tant les classes com les funcions constructor imiten un model d’herència orientat cap a objectes per a JavaScript, que és un llenguatge d’herència basat en prototips.
Comprendre l’herència prototípica és primordial per a exercir-se amb eficàcia com a desenvolupador de JavaScript. Se amb les classes és extremadament útil, ja que a les biblioteques de JavaScript populars com React s’usa amb freqüència la sintaxi class
.