Vi algumas respostas semelhantes, mas gostaria de mencionar que este post descreve melhor, por isso, gostaria de compartilhar com você.
Aqui estão alguns códigos retirados dele, que modifiquei para obter um exemplo completo que, esperamos, traga benefícios à comunidade, pois pode ser usado como um modelo de design para as classes.
Ele também responde à sua pergunta:
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 esse exemplo, você pode acessar as propriedades / funções estáticas da seguinte maneira:
// access static properties/functions
console.log(Podcast.FILE_EXTENSION); // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
E as propriedades / funções do objeto simplesmente 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
Observe que em podcast.immutableProp (), temos um fechamento : A referência a _somePrivateVariable é mantida dentro da função.
Você pode até definir getters e setters . Dê uma olhada neste snippet de código (onde d
é o protótipo do objeto para o qual você deseja declarar uma propriedade, y
é uma variável privada não visível fora do construtor):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
Ele define a propriedade d.year
via get
e set
funções - se você não especificar set
, a propriedade é somente leitura e não pode ser modificada (lembre-se de que você não receberá um erro se tentar defini-la, mas não terá efeito). Cada propriedade tem os atributos writable
, configurable
(permitir que a mudança após a declaração) e enumerable
(permitem usá-lo como recenseador), que são por padrão false
. Você pode configurá-los via defineProperty
no terceiro parâmetro, por exemplo enumerable: true
.
O que também é válido é esta sintaxe:
// getters and setters - alternative syntax
var obj = { a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}
};
que define uma propriedade legível / gravável a
, uma propriedade somente leitura b
e uma propriedade somente gravação c
, através da qual a propriedadea
pode ser acessada.
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 comportamentos inesperados, caso você tenha esquecido a new
palavra - chave, sugiro que você adicione o seguinte à função Podcast
:
// instantiation helper
function Podcast() {
if(false === (this instanceof Podcast)) {
return new Podcast();
}
// [... same as above ...]
};
Agora, as duas instanciações a seguir funcionarão conforme o esperado:
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast(); // you can omit the new keyword because of the helper
A instrução 'new' cria um novo objeto e copia todas as propriedades e métodos, ou seja,
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"
Observe também que , em algumas situações, pode ser útil usar a return
instrução na função construtora Podcast
para retornar um objeto personalizado que protege funções nas quais a classe confia internamente, mas que precisa ser exposta. Isso é explicado mais adiante no capítulo 2 (Objetos) da série de artigos.
Você pode dizer isso a
e b
herdar de Podcast
. Agora, e se você quiser adicionar ao Podcast um método que se aplique a todos eles depois a
e b
tenha sido instanciado? Nesse caso, use o .prototype
seguinte:
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
Agora ligue a
e b
novamente:
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
Você pode encontrar mais detalhes sobre protótipos aqui . Se você deseja fazer mais herança, sugiro investigar isso .
As séries de artigos que mencionei acima são altamente recomendadas para leitura, incluem também os seguintes tópicos:
- Funções
- Objetos
- Protótipos
- Aplicação de novas funções de construtor
- Elevação
- Inserção automática de ponto e vírgula
- Propriedades e métodos estáticos
Observe que o "recurso" automático de inserção de ponto e vírgula do JavaScript (conforme mencionado em 6.) é muitas vezes responsável por causar problemas estranhos no seu código. Por isso, prefiro considerá-lo um bug do que um recurso.
Se você quiser ler mais, aqui está um artigo interessante do MSDN sobre esses tópicos, alguns deles descritos aqui fornecem ainda mais detalhes.
O que é interessante de ler também (também abordando os tópicos mencionados acima) são os artigos do MDN JavaScript Guide :
Se você quiser saber como emular out
parâmetros c # (como em DateTime.TryParse(str, out result)
) em JavaScript, poderá encontrar um exemplo de código aqui.
Aqueles que estão trabalhando com o IE (que não tem console para JavaScript, a menos que você abra as ferramentas de desenvolvedor usando F12e abra a guia console) podem achar útil o seguinte trecho. Ele permite que você use console.log(msg);
como usado nos exemplos acima. Basta inseri-lo antes da Podcast
função.
Para sua conveniência, veja o código acima em um único trecho de código 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:
Algumas boas dicas, sugestões e recomendações sobre programação JavaScript em geral, você pode encontrar aqui (práticas recomendadas para JavaScript) e ali ('var' versus 'let') . Também é recomendado este artigo sobre previsões implícitas (coerção) .
Uma maneira conveniente de usar classes e compilá-las em JavaScript é o TypeScript. Aqui está um playground onde você pode encontrar alguns exemplos mostrando como funciona. Mesmo que você não esteja usando o TypeScript no momento, é possível ver porque você pode comparar o TypeScript com o resultado do JavaScript em uma exibição lado a lado. A maioria dos exemplos é simples, mas também há um exemplo do Raytracer que você pode experimentar instantaneamente. Eu recomendo olhar especialmente para os exemplos "Using Classes", "Using Inheritance" e "Using Generics" selecionando-os na caixa de combinação - esses são bons modelos que você pode usar instantaneamente em JavaScript. O texto datilografado é usado com Angular.
Para obter o encapsulamento de variáveis locais, funções etc. em JavaScript, sugiro usar um padrão como o seguinte (o JQuery usa a mesma 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>
Obviamente, você pode - e deve - colocar o código do script em um *.js
arquivo separado ; isso é apenas escrito em linha para manter o exemplo curto.
As funções de auto-invocação (também conhecidas como IIFE = Expressão de Função Imediatamente Invocada) são descritas em mais detalhes aqui .