He visto un par de respuestas similares, pero me gustaría mencionar que esta publicación lo describe mejor, así que me gustaría compartirlo con ustedes.
Aquí hay un código extraído de él, que he modificado para obtener un ejemplo completo que, con suerte, beneficiará a la comunidad porque se puede usar como plantilla de diseño para las clases.
También responde a tu pregunta:
function Podcast() {
// private variables
var _somePrivateVariable = 123;
// object properties (read/write)
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
// for read access to _somePrivateVariable via immutableProp
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
Dado ese ejemplo, puede acceder a las propiedades / funciones estáticas de la siguiente manera:
// access static properties/functions
console.log(Podcast.FILE_EXTENSION); // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
Y las propiedades / funciones del objeto simplemente como:
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
Tenga en cuenta que en podcast.immutableProp (), tenemos un cierre : la referencia a _somePrivateVariable se mantiene dentro de la función.
Incluso puede definir captadores y establecedores . Eche un vistazo a este fragmento de código (donde d
está el prototipo del objeto para el que desea declarar una propiedad, y
es una variable privada no visible fuera del constructor):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
Define la propiedad a d.year
través de get
y set
funciones: si no especifica set
, entonces la propiedad es de solo lectura y no puede modificarse (tenga en cuenta que no obtendrá un error si intenta configurarlo, pero no tiene ningún efecto). Cada propiedad tiene los atributos writable
, configurable
(permitir al cambio después de la declaración) y enumerable
(permite utilizarlo como empadronador), que son por defecto false
. Puede configurarlos defineProperty
en el tercer parámetro, por ejemplo enumerable: true
.
Lo que también es válido es esta sintaxis:
// getters and setters - alternative syntax
var obj = { a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}
};
que define una propiedad legible / grabable a
, una propiedad de solo lectura b
y una propiedad de solo escritura c
, a través de la cual a
se puede acceder a la propiedad .
Uso:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
Notas:
Para evitar un comportamiento inesperado en caso de que haya olvidado la new
palabra clave, le sugiero que agregue lo siguiente a la función Podcast
:
// instantiation helper
function Podcast() {
if(false === (this instanceof Podcast)) {
return new Podcast();
}
// [... same as above ...]
};
Ahora las dos instancias siguientes funcionarán como se esperaba:
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast(); // you can omit the new keyword because of the helper
La declaración 'nuevo' crea un nuevo objeto y copia todas las propiedades y métodos, es decir
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Tenga en cuenta también que , en algunas situaciones, puede ser útil utilizar la return
instrucción en la función de constructor Podcast
para devolver un objeto personalizado que protege las funciones de las que depende internamente la clase pero que necesitan ser expuestas. Esto se explica más adelante en el capítulo 2 (Objetos) de la serie de artículos.
Puedes decir eso a
y b
heredar de Podcast
. Ahora, ¿qué sucede si desea agregar un método a Podcast que se aplique a todos ellos después a
y b
haya sido instanciado? En este caso, use lo .prototype
siguiente:
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
Ahora llame a
y b
otra vez:
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
Puede encontrar más detalles sobre los prototipos aquí . Si desea hacer más herencia, le sugiero que investigue esto .
Se recomienda encarecidamente leer la serie de artículos que he mencionado anteriormente , que incluyen también los siguientes temas:
- Las funciones
- Objetos
- Prototipos
- Hacer cumplir nuevas funciones de constructor
- Elevación
- Inserción automática de punto y coma
- Propiedades y métodos estáticos
Tenga en cuenta que la "característica" de inserción automática de punto y coma de JavaScript (como se menciona en 6.) a menudo es responsable de causar problemas extraños en su código. Por lo tanto, prefiero considerarlo como un error que como una característica.
Si desea leer más, aquí hay un artículo de MSDN bastante interesante sobre estos temas, algunos de ellos descritos allí brindan aún más detalles.
Lo que también es interesante de leer (que también cubre los temas mencionados anteriormente) son los artículos de la Guía de JavaScript de MDN :
Si desea saber cómo emular out
parámetros de C # (como en DateTime.TryParse(str, out result)
) en JavaScript, puede encontrar el código de muestra aquí.
Aquellos de ustedes que están trabajando con IE (que no tiene consola para JavaScript a menos que abra las herramientas de desarrollador usando F12y abra la pestaña de la consola) pueden encontrar útil el siguiente fragmento. Le permite usar console.log(msg);
como se usa en los ejemplos anteriores. Simplemente insértelo antes de la Podcast
función.
Para su comodidad, aquí está el código anterior en un fragmento de código único completo:
let console = { log: function(msg) {
let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
canvas.innerHTML += (br + (msg || "").toString());
}};
console.log('For details, see the explaining text');
function Podcast() {
// with this, you can instantiate without new (see description in text)
if (false === (this instanceof Podcast)) {
return new Podcast();
}
// private variables
var _somePrivateVariable = 123;
// object properties
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {
return this.getFullYear()
},
set: function(y) {
this.setFullYear(y)
}
});
// getters and setters - alternative syntax
var obj = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
<div id="log"></div>
Notas:
Algunos buenos consejos, sugerencias y recomendaciones sobre la programación de JavaScript en general se pueden encontrar aquí (prácticas recomendadas de JavaScript) y allí ('var' versus 'let') . También se recomienda este artículo sobre los tipos de letra implícitos (coerción) .
Una forma conveniente de usar clases y compilarlas en JavaScript es TypeScript. Aquí hay un patio de juegos donde puedes encontrar algunos ejemplos que te muestran cómo funciona. Incluso si no está utilizando TypeScript en este momento, puede echar un vistazo porque puede comparar TypeScript con el resultado de JavaScript en una vista de lado a lado. La mayoría de los ejemplos son simples, pero también hay un ejemplo de Raytracer que puede probar al instante. Recomiendo especialmente buscar en los ejemplos de "Uso de clases", "Uso de herencia" y "Uso de genéricos" seleccionándolos en el cuadro combinado: estas son plantillas agradables que puede usar instantáneamente en JavaScript. La mecanografía se usa con Angular.
Para lograr la encapsulación de variables locales, funciones, etc. en JavaScript, sugiero usar un patrón como el siguiente (JQuery usa la misma técnica):
<html>
<head></head>
<body><script>
'use strict';
// module pattern (self invoked function)
const myModule = (function(context) {
// to allow replacement of the function, use 'var' otherwise keep 'const'
// put variables and function with local module scope here:
var print = function(str) {
if (str !== undefined) context.document.write(str);
context.document.write("<br/><br/>");
return;
}
// ... more variables ...
// main method
var _main = function(title) {
if (title !== undefined) print(title);
print("<b>last modified: </b>" + context.document.lastModified + "<br/>");
// ... more code ...
}
// public methods
return {
Main: _main
// ... more public methods, properties ...
};
})(this);
// use module
myModule.Main("<b>Module demo</b>");
</script></body>
</html>
Por supuesto, puede y debe colocar el código del script en un *.js
archivo separado ; esto solo está escrito en línea para mantener el ejemplo corto.
Las funciones de auto invocación (también conocidas como IIFE = Expresión de función invocada inmediatamente) se describen con más detalle aquí .