Gostaria de saber qual é a melhor maneira de criar um objeto JavaScript que tenha propriedades e métodos.
Eu já vi exemplos em que a pessoa usou var self = this
e depois usa self.
em todas as funções para garantir que o escopo esteja sempre correto.
Então eu vi exemplos de .prototype
como adicionar propriedades, enquanto outros o fazem em linha.
Alguém pode me dar um exemplo adequado de um objeto JavaScript com algumas propriedades e métodos?
javascript
Michael Stum
fonte
fonte
self
uma palavra reservada? Se não, deveria ser; uma vez queself
é uma variável predefinida referente à janela atual.self === window
window
modelo de objeto do navegador, comodocument
ouframes
; você certamente pode reutilizar o identificador como um nome de variável. Embora, sim, estilisticamente prefirovar that= this
evitar qualquer possível confusão. Mesmo quewindow.self
seja inútil, raramente há motivo para tocá-lo.this
a uma variável local (por exemploself
) reduz o tamanho do arquivo.Respostas:
Existem dois modelos para implementar classes e instâncias no JavaScript: a forma de prototipagem e a forma de fechamento. Ambos têm vantagens e desvantagens, e há muitas variações estendidas. Muitos programadores e bibliotecas têm abordagens diferentes e funções utilitárias de manipulação de classes para encobrir algumas das partes mais feias da linguagem.
O resultado é que, em empresas mistas, você terá uma mistura de metaclasses, todos se comportando de maneira um pouco diferente. O que é pior, a maioria dos materiais de tutoriais sobre JavaScript é terrível e serve como uma espécie de compromisso intermediário para cobrir todas as bases, deixando você muito confuso. (Provavelmente o autor também está confuso. O modelo de objeto do JavaScript é muito diferente da maioria das linguagens de programação e, em muitos lugares, mal projetado.)
Vamos começar com o protótipo . Este é o mais nativo do JavaScript que você pode obter: há um mínimo de código adicional e instanceof funcionará com instâncias desse tipo de objeto.
Podemos adicionar métodos à instância criada
new Shape
escrevendo-os naprototype
pesquisa dessa função construtora:Agora, para subclassificá-lo, o máximo que você pode chamar de JavaScript faz subclassificação. Fazemos isso substituindo completamente essa
prototype
propriedade mágica estranha :antes de adicionar métodos a ele:
Este exemplo funcionará e você verá o código semelhante em muitos tutoriais. Mas cara, isso
new Shape()
é feio: estamos instanciando a classe base, mesmo que nenhuma Forma real seja criada. Isso acontece com o trabalho neste caso simples, porque o JavaScript é tão desleixada: permite zero de argumentos a serem passados em, caso em quex
ey
tornam-seundefined
e são atribuídos ao protótipo dethis.x
ethis.y
. Se a função construtora estivesse fazendo algo mais complicado, ela cairia de cara no chão.Portanto, o que precisamos fazer é encontrar uma maneira de criar um objeto protótipo que contenha os métodos e outros membros que desejamos no nível da classe, sem chamar a função construtora da classe base. Para fazer isso, teremos que começar a escrever o código auxiliar. Esta é a abordagem mais simples que conheço:
Isso transfere os membros da classe base em seu protótipo para uma nova função construtora que não faz nada e depois usa esse construtor. Agora podemos escrever simplesmente:
em vez do
new Shape()
erro. Agora temos um conjunto aceitável de primitivas para as classes construídas.Existem alguns refinamentos e extensões que podemos considerar nesse modelo. Por exemplo, aqui está uma versão sintática do açúcar:
Qualquer uma das versões tem a desvantagem de que a função construtora não pode ser herdada, como ocorre em muitos idiomas. Portanto, mesmo que sua subclasse não adicione nada ao processo de construção, lembre-se de chamar o construtor de base com quaisquer argumentos que a base desejasse. Isso pode ser um pouco automatizado
apply
, mas você ainda precisa escrever:Portanto, uma extensão comum é dividir o material de inicialização em sua própria função, e não no próprio construtor. Esta função pode herdar da base muito bem:
Agora, temos o mesmo padrão de função de construtor para cada classe. Talvez possamos colocar isso em sua própria função auxiliar, para que não tenhamos que continuar digitando, por exemplo, em vez de
Function.prototype.subclass
girá-lo e deixar que a Função da classe base cuspa subclasses:... que está começando a parecer um pouco mais com outros idiomas, embora com sintaxe um pouco desajeitada. Você pode polvilhar alguns recursos extras, se quiser. Talvez você queira
makeSubclass
pegar e lembrar um nome de classe e fornecer um padrãotoString
usando-o. Talvez você queira que o construtor detecte quando foi chamado acidentalmente sem onew
operador (o que normalmente resultaria em depuração muito irritante):Talvez você queira passar todos os novos membros e
makeSubclass
adicioná-los ao protótipo, para poupar a necessidade de escreverClass.prototype...
muito. Muitos sistemas de classe fazem isso, por exemplo:Existem muitos recursos em potencial que você pode considerar desejáveis em um sistema de objetos e ninguém realmente concorda com uma fórmula específica.
O caminho do fechamento , então. Isso evita os problemas da herança baseada em protótipo do JavaScript, ao não usar a herança. Em vez de:
Agora, cada instância única
Shape
terá sua própria cópia dotoString
método (e quaisquer outros métodos ou outros membros da classe que adicionarmos).O lado ruim de toda instância ter sua própria cópia de cada aluno é que é menos eficiente. Se você estiver lidando com um grande número de instâncias subclassificadas, a herança prototípica poderá atendê-lo melhor. Também chamar um método da classe base é um pouco irritante, como você pode ver: precisamos lembrar qual era o método antes que o construtor da subclasse o substituísse ou se perdesse.
[Também porque não há herança aqui, o
instanceof
operador não funcionará; você precisaria fornecer seu próprio mecanismo de detecção de classe, se necessário. Embora você possa mexer nos objetos do protótipo de maneira semelhante à herança do protótipo, é um pouco complicado e realmente não vale a pena apenas para começar ainstanceof
trabalhar.]O bom de toda instância ter seu próprio método é que o método pode ser vinculado à instância específica que o possui. Isso é útil devido à maneira estranha de ligação do JavaScript
this
nas chamadas de método, que tem o resultado de que, se você desanexar um método do proprietário:então,
this
dentro do método, a instância Circle não será a esperada (na verdade, será owindow
objeto global , causando um problema de depuração generalizado). Na realidade, isso normalmente acontece quando um método é usado e atribuído a umsetTimeout
,onclick
ouEventListener
em geral.Com o protótipo, você deve incluir um fechamento para cada tarefa:
ou, no futuro (ou agora, se você hackear Function.prototype), você também poderá fazê-lo com
function.bind()
:se suas instâncias são feitas da maneira de fechamento, a ligação é feita gratuitamente pelo fechamento da variável de instância (geralmente chamada
that
ouself
, embora pessoalmente eu recomendaria contra essa última,self
já que ela tem outro significado diferente em JavaScript). No1, 1
entanto, você não obtém os argumentos do snippet acima gratuitamente, portanto ainda precisaria de outro fechamento ou a,bind()
se precisar fazer isso.Existem muitas variantes no método de fechamento também. Você pode preferir omitir
this
completamente, criando um novothat
e retornando-o em vez de usar onew
operador:Qual o caminho é "adequado"? Ambos. Qual é melhor"? Isso depende da sua situação. FWIW: Eu tendem a criar protótipos para herança de JavaScript real quando estou fazendo coisas fortemente com OO e fechamentos para simples efeitos de página descartáveis.
Mas ambas as formas são bastante contra-intuitivas para a maioria dos programadores. Ambos têm muitas variações confusas em potencial. Você encontrará ambos (assim como muitos esquemas intermediários e geralmente interrompidos) se usar o código / biblioteca de outras pessoas. Não existe uma resposta geralmente aceita. Bem-vindo ao maravilhoso mundo dos objetos JavaScript.
[Isso foi parte 94 de Por que o JavaScript não é minha linguagem de programação favorita.]
fonte
new
.Uso esse padrão com bastante frequência - descobri que ele me dá uma quantidade enorme de flexibilidade quando preciso. Em uso, é bastante semelhante às classes no estilo Java.
Isso usa uma função anônima chamada na criação, retornando uma nova função construtora. Como a função anônima é chamada apenas uma vez, é possível criar variáveis estáticas privadas nela (elas estão dentro do fechamento, visíveis para os outros membros da classe). A função construtora é basicamente um objeto Javascript padrão - você define atributos privados dentro dela e atributos públicos são anexados à
this
variável.Basicamente, essa abordagem combina a abordagem Crockfordian com objetos Javascript padrão para criar uma classe mais poderosa.
Você pode usá-lo como faria com qualquer outro objeto Javascript:
fonte
this
, ele ainda precisa ser instanciadonew
?this
é usado naconstructor
função, que se tornaFoo
no exemplo.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford discute esse tópico extensivamente em The Good Parts . Ele recomenda evitar que o novo operador crie novos objetos. Em vez disso, ele propõe criar construtores personalizados. Por exemplo:
Em Javascript, uma função é um objeto e pode ser usada para construir objetos em conjunto com o novo operador. Por convenção, as funções destinadas a serem usadas como construtores começam com uma letra maiúscula. Você costuma ver coisas como:
Caso você se esqueça de usar o novo operador enquanto instancia um novo objeto, o que você recebe é uma chamada de função comum, e isso é vinculado ao objeto global e não ao novo objeto.
fonte
new
devar that = {};
qualquer maneira.Para continuar fora da resposta de bobince
Agora, no es6, você pode criar um
class
Então agora você pode fazer:
Portanto, estenda para um círculo (como na outra resposta) que você pode:
Termina um pouco mais limpo no es6 e um pouco mais fácil de ler.
Aqui está um bom exemplo disso em ação:
Mostrar snippet de código
fonte
Você também pode fazer isso dessa maneira, usando estruturas:
Então :
fonte
Outra maneira seria http://jsfiddle.net/nnUY4/ (não sei se esse tipo de manipulação de funções de criação e revelação de objetos segue algum padrão específico)
fonte
Quando alguém usa o truque para fechar "this" durante uma chamada de construtor, é para escrever uma função que pode ser usada como retorno de chamada por algum outro objeto que não deseja chamar um método em um objeto. Não está relacionado a "tornar o escopo correto".
Aqui está um objeto JavaScript baunilha:
Você pode ler muito o que Douglas Crockford tem a dizer sobre JavaScript. John Resig também é brilhante. Boa sorte!
fonte
this
tem tudo a ver com "tornar o escopo correto".self=this
embora não forcethis
a persistir, permite facilmente o escopo "correto" por meio de um fechamento.Closure
é versátil. bobince resumiu bem as abordagens protótipo versus fechamento ao criar objetos. No entanto, você pode imitar alguns aspectos doOOP
uso do fechamento de uma maneira de programação funcional. Lembre-se de funções são objetos em JavaScript ; então use a função como objeto de uma maneira diferente.Aqui está um exemplo de encerramento:
Há um tempo atrás, me deparei com o artigo da Mozilla sobre fechamento. Aqui está o que pulo aos meus olhos: "Um fechamento permite associar alguns dados (o ambiente) a uma função que opera nesses dados. Isso tem paralelos óbvios com a programação orientada a objetos, onde objetos nos permitem associar alguns dados (as propriedades do objeto ) com um ou mais métodos ". Foi a primeira vez que li um paralelismo entre o fechamento e o POO clássico, sem referência ao protótipo.
Quão?
Suponha que você queira calcular o IVA de alguns itens. É provável que o IVA permaneça estável durante a vida útil de um aplicativo. Uma maneira de fazer isso no OOP (pseudo código):
Basicamente, você transmite um valor de IVA ao seu construtor e seu método de cálculo pode operar com ele através do fechamento . Agora, em vez de usar uma classe / construtor, passe seu IVA como argumento para uma função. Como o único material de seu interesse é o próprio cálculo, retorna uma nova função, que é o método de cálculo:
No seu projeto, identifique valores de nível superior que sejam bons candidatos ao cálculo do IVA. Como regra geral, sempre que você passa os mesmos argumentos, há uma maneira de aprimorá-lo usando o fechamento. Não há necessidade de criar objetos tradicionais.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
fonte
Criando um objeto
A maneira mais fácil de criar um objeto em JavaScript é usar a seguinte sintaxe:
Isso funciona muito bem para armazenar dados de maneira estruturada.
Para casos de uso mais complexos, no entanto, geralmente é melhor criar instâncias de funções:
Isso permite criar vários objetos que compartilham o mesmo "blueprint", semelhante à maneira como você usa as classes, por exemplo. Java.
No entanto, isso ainda pode ser feito com mais eficiência usando um protótipo.
Sempre que diferentes instâncias de uma função compartilham os mesmos métodos ou propriedades, você pode movê-las para o protótipo desse objeto. Dessa forma, toda instância de uma função tem acesso a esse método ou propriedade, mas não precisa ser duplicada para cada instância.
No nosso caso, faz sentido mover o método
f
para o protótipo:Herança
Uma maneira simples, porém eficaz, de fazer herança em JavaScript, é usar o seguinte recurso de duas linhas:
Isso é semelhante a fazer isso:
A principal diferença entre ambos é que o construtor de
A
não é executado ao usarObject.create
, o que é mais intuitivo e mais semelhante à herança baseada em classe.Você sempre pode optar por executar opcionalmente o construtor
A
ao criar uma nova instância deB
, adicionando-a ao construtor deB
:Se você deseja passar todos os argumentos de
B
paraA
, também pode usarFunction.prototype.apply()
:Se você deseja misturar outro objeto na cadeia de construtores de
B
, você pode combinarObject.create
comObject.assign
:Demo
Nota
Object.create
pode ser usado com segurança em todos os navegadores modernos, incluindo o IE9 +.Object.assign
não funciona em nenhuma versão do IE nem em alguns navegadores móveis. Recomenda-se policarObject.create
e / ouObject.assign
se você deseja usá-los e oferecer suporte a navegadores que não os implementam.Você pode encontrar um polyfill para
Object.create
aqui e outro paraObject.assign
aqui .fonte
fonte
Além da resposta aceita de 2009. Se você pode segmentar navegadores modernos, pode-se usar o Object.defineProperty .
Para ver uma lista de compatibilidade com computadores e dispositivos móveis, consulte a lista de compatibilidade do navegador Mozilla . Sim, o IE9 + é compatível com o Safari Mobile.
fonte
Você também pode tentar isso
fonte
Primeiro, você pode alterar sua preferência de adicionar métodos à instância em vez do
prototype
objeto do construtor . Quase sempre declaro métodos dentro do construtor porque uso o seqüestro de construtor muita frequência para fins de Herança e Decoradores.Aqui está como eu decido onde as declarações serão escritas:
this
)var
declarações tenham precedência sobrefunction
declarações{}
e[]
)public
declarações tenham precedência sobreprivate
declaraçõesFunction.prototype.bind
sobrethus
,self
,vm
,etc
this
de dentro do escopo léxico do espaço de fechamento.Aqui está o porquê destes ajudarem:
Seqüestro de construtor Model Design Padrões de designComo você pode ver, eu realmente não preciso disso,
thus
já que prefiroFunction.prototype.bind
(ou.call
ou.apply
)thus
. Em nossaSingleton
classe, nem o nomeamos,thus
porqueINSTANCE
transmite mais informações. ParaModel
, retornamosthis
para que possamos chamar o Construtor usando.call
para retornar a instância que passamos para ele. Redundantemente, nós a atribuímos à variávelupdated
, embora seja útil em outros cenários.Além disso, prefiro construir literais de objetos usando o método
Preferido Não preferidonew
palavra-chave em vez de {colchetes}:Como você pode ver, o último não tem capacidade de substituir a propriedade "substituir" da sua superclasse.
Se eu adicionar métodos ao
Preferido Não preferidoprototype
objeto da classe , prefiro um literal de objeto - com ou sem o uso danew
palavra-chave:fonte
Gostaria de mencionar que podemos usar um Título ou uma String para declarar um Objeto.
Existem diferentes maneiras de chamar cada tipo deles. Ver abaixo:
fonte
Basicamente, não existe um conceito de classe no JS; portanto, usamos a função como construtor de classe relevante para os padrões de design existentes.
Até agora, JS não tem idéia de que você deseja criar um objeto, então aqui vem a nova palavra-chave.
Ref: JS profissional para desenvolvedores web - Nik Z
fonte
class
em JS, como você mencionou em seu título usando afunction
palavra-chave. É não um padrão de design, mas uma característica intencional da linguagem. Não votei contra você nisso, mas parece que alguém o fez por causa da discrepância e quase irrelevância para a pergunta. Espero que esse feedback ajude.