Existe alguma maneira de tornar variáveis "privadas" (aquelas definidas no construtor) disponíveis para métodos definidos por protótipo?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
Isso funciona:
t.nonProtoHello()
Mas isso não acontece:
t.prototypeHello()
Estou acostumado a definir meus métodos dentro do construtor, mas estou me afastando disso por alguns motivos.
javascript
private-members
morgancodes
fonte
fonte
Respostas:
Não, não há como fazê-lo. Isso seria essencialmente o escopo inverso.
Os métodos definidos dentro do construtor têm acesso a variáveis privadas porque todas as funções têm acesso ao escopo em que foram definidas.
Os métodos definidos em um protótipo não são definidos no escopo do construtor e não terão acesso às variáveis locais do construtor.
Você ainda pode ter variáveis privadas, mas se quiser que os métodos definidos no protótipo tenham acesso a eles, defina getters e setters no
this
objeto, aos quais os métodos do protótipo (junto com todo o resto) terão acesso. Por exemplo:fonte
secret
uma propriedade dethis
. O JavaScript simplesmente não suporta variáveis privadas com protótipos, pois os protótipos estão vinculados ao contexto do site de chamada, não ao contexto 'site de criação'.person.getSecret()
então?Atualização: Com o ES6, existe uma maneira melhor:
Para encurtar a história, você pode usar o novo
Symbol
para criar campos particulares.Aqui está uma ótima descrição: https://curiosity-driven.org/private-properties-in-javascript
Exemplo:
Para todos os navegadores modernos com ES5:
Você pode usar apenas Closures
A maneira mais simples de construir objetos é evitar a herança prototípica completamente. Apenas defina as variáveis privadas e funções públicas no fechamento, e todos os métodos públicos terão acesso privado às variáveis.
Ou você pode usar apenas protótipos
Em JavaScript, a herança prototípica é principalmente uma otimização . Ele permite que várias instâncias compartilhem métodos de protótipo, em vez de cada instância ter seus próprios métodos.
A desvantagem é que
this
é a única coisa diferente sempre que uma função prototípica é chamada.Portanto, todos os campos particulares devem ser acessíveis através
this
, o que significa que eles serão públicos. Portanto, mantemos as convenções de nomenclatura para_private
campos.Não se preocupe em misturar closures com protótipos
Eu acho que você não deve misturar variáveis de fechamento com métodos de protótipo. Você deve usar um ou outro.
Quando você usa um fechamento para acessar uma variável privada, os métodos de protótipo não podem acessar a variável. Então, você precisa expor o fechamento
this
, o que significa que você o está expondo publicamente de uma maneira ou de outra. Há muito pouco a ganhar com essa abordagem.Qual eu escolho?
Para objetos realmente simples, basta usar um objeto simples com tampas.
Se você precisar de herança prototípica - por herança, desempenho etc. -, siga a convenção de nomenclatura "_private" e não se preocupe com os fechamentos.
Não entendo por que os desenvolvedores de JS se esforçam tanto para tornar os campos verdadeiramente privados.
fonte
_private
convenção de nomenclatura ainda é a melhor solução se você quiser tirar proveito da herança prototípica.Symbol
, que é uma excelente maneira de criar campos particulares. Aqui está uma ótima explicação: curiosity-driven.org/private-properties-in-javascriptSymbol
fechamento que abrange toda a classe. Dessa forma, todos os métodos de protótipo podem usar o Symbol, mas ele nunca é exposto fora da classe.Object.getOwnPropertySymbols
. Portanto, isso é apenas privacidade pela obscuridade.toString
. Isso não é diferente de Java ou C # ... membros privados ainda estão acessíveis via reflexão, mas geralmente são fortemente obscurecidos. Tudo isso reforça meu argumento final: "Não entendo por que os desenvolvedores de JS se esforçam tanto para tornar os campos verdadeiramente privados".Quando li isso, parecia um desafio difícil, então decidi descobrir uma maneira. O que eu criei foi CRAAAAZY, mas funciona totalmente.
Primeiro, tentei definir a classe em uma função imediata para que você tivesse acesso a algumas das propriedades particulares dessa função. Isso funciona e permite que você obtenha alguns dados particulares; no entanto, se você tentar definir os dados particulares, em breve descobrirá que todos os objetos compartilharão o mesmo valor.
Existem muitos casos em que isso seria adequado, como se você quisesse ter valores constantes, como nomes de eventos, que são compartilhados entre instâncias. Mas, essencialmente, eles agem como variáveis estáticas privadas.
Se você absolutamente precisar acessar variáveis em um espaço para nome privado de dentro de seus métodos definidos no protótipo, tente este padrão.
Eu adoraria receber comentários de quem vê um erro nessa maneira de fazê-lo.
fonte
i
foi adicionado a todas as instâncias. Portanto, não é totalmente "transparente" ei
ainda pode ser adulterado.veja a página de Doug Crockford sobre isso . Você precisa fazer isso indiretamente com algo que possa acessar o escopo da variável privada.
outro exemplo:
caso de uso:
fonte
_set
viaset
? Por que não apenas nomeá-loset
para começar?Sugiro que provavelmente seria uma boa ideia descrever "ter uma atribuição de protótipo em um construtor" como um antipadrão de Javascript. Pense nisso. É muito arriscado.
O que você está fazendo ali na criação do segundo objeto (ou seja, b) está redefinindo a função protótipo para todos os objetos que usam esse protótipo. Isso redefinirá efetivamente o valor do objeto a no seu exemplo. Funcionará se você desejar uma variável compartilhada e se criar todas as instâncias de objeto antecipadamente, mas parecerá muito arriscado.
Eu encontrei um bug em algum Javascript em que eu estava trabalhando recentemente devido a esse exato anti-padrão. Ele estava tentando definir um manipulador de arrastar e soltar no objeto específico que estava sendo criado, mas estava fazendo isso em todas as instâncias. Não é bom.
A solução de Doug Crockford é a melhor.
fonte
@Kai
Isso não vai funcionar. Se você fizer
então
t2.prototypeHello
acessará a seção privada de t.@AnglesCrimes
O código de exemplo funciona bem, mas na verdade cria um membro privado "estático" compartilhado por todas as instâncias. Pode não ser a solução que os morgancodes procuraram.
Até agora, não encontrei uma maneira fácil e limpa de fazer isso sem introduzir um hash privado e funções de limpeza extras. Uma função de membro privado pode ser simulada até certo ponto:
fonte
privateFoo
é completamente privado e, portanto, invisível ao obter umnew Foo()
. Somente aquibar()
é um método público que tem acessoprivateFoo
. Você pode usar o mesmo mecanismo para variáveis e objetos simples, no entanto, sempre lembre-se de que elesprivates
são realmente estáticos e serão compartilhados por todos os objetos que você criar.Sim é possivel. O padrão de design do PPF resolve isso.
PPF significa Funções de protótipo privadas. O PPF básico resolve esses problemas:
Para o primeiro, apenas:
É simples assim. Por exemplo:
...
Leia a história completa aqui:
Padrão de Design PPF
fonte
Você pode realmente conseguir isso usando a Verificação de acessador :
Este exemplo vem do meu post sobre Funções Prototípicas e Dados Privados e é explicado com mais detalhes por lá.
fonte
Na atual JavaScript, estou bastante certo de que há uma e apenas uma maneira de ter estado privado , acessível a partir de protótipos de funções, sem acrescentar nada público para
this
. A resposta é usar o padrão "mapa fraco".Para resumir: A
Person
classe tem um único mapa fraco, onde as chaves são as instâncias de Person e os valores são objetos simples que são usados para armazenamento privado.Aqui está um exemplo totalmente funcional: (jogue em http://jsfiddle.net/ScottRippey/BLNVr/ )
Como eu disse, essa é realmente a única maneira de obter todas as três partes.
Existem duas advertências, no entanto. Primeiro, isso custa desempenho - sempre que você acessa os dados privados, é uma
O(n)
operação, onden
está o número de instâncias. Portanto, você não vai querer fazer isso se tiver um grande número de instâncias. Segundo, quando você terminar uma instância, você deve chamardestroy
; caso contrário, a instância e os dados não serão coletados como lixo e você terá um vazamento de memória.E é por isso que minha resposta original, "Você não deveria" , é algo que eu gostaria de seguir.
fonte
Há uma maneira mais simples, aproveitando o uso de
bind
ecall
métodos.Ao definir variáveis privadas para um objeto, você pode aproveitar o escopo desse objeto.
Exemplo
Este método não apresenta desvantagens. Como o contexto do escopo está sendo efetivamente substituído, você não tem acesso fora do
_private
objeto. No entanto, não é impossível ainda dar acesso ao escopo do objeto de instância. Você pode passar no contexto do objeto (this
) como o segundo argumento parabind
oucall
ainda ter acesso aos seus valores públicos na função prototype.Acessando valores públicos
fonte
Tente!
fonte
caller
, que é uma extensão dependente de implementação não permitida no modo estrito.Aqui está o que eu criei.
o principal problema dessa implementação é que ela redefine os protótipos em todas as instanciações.
fonte
Existe uma maneira muito simples de fazer isso
Os protótipos JavaScript são dourados.
fonte
SharedPrivate.prototype
quantothis.constructor.prototype
Ele não é grande coisa para redefinir GETP e SETP várias vezes ...Estou atrasado para a festa, mas acho que posso contribuir. Aqui, verifique isso:
Eu chamo esse padrão de acessador de método . A idéia essencial é que tenhamos um fechamento , uma chave dentro do fechamento, e criamos um objeto privado (no construtor) que só pode ser acessado se você tiver a chave .
Se você estiver interessado, pode ler mais sobre isso no meu artigo . Usando esse método, você pode criar propriedades por objeto que não podem ser acessadas fora do fechamento. Portanto, você pode usá-los no construtor ou protótipo, mas não em nenhum outro lugar. Não vi esse método usado em nenhum lugar, mas acho que é realmente poderoso.
fonte
Você não pode colocar as variáveis em um escopo mais alto?
fonte
Você também pode tentar adicionar o método não diretamente no protótipo, mas na função construtora como esta:
fonte
Aqui está uma coisa que eu inventei ao tentar encontrar a solução mais simples para esse problema, talvez possa ser útil para alguém. Eu sou novo em javascript, portanto, pode haver alguns problemas com o código.
fonte
Enfrentei exatamente a mesma pergunta hoje e, depois de elaborar a resposta de primeira classe de Scott Rippey, criei uma solução muito simples (IMHO), compatível com ES5 e eficiente, que também é segura contra conflitos de nome (usar _private parece inseguro) .
Testado com ringojs e nodejs. Estou ansioso para ler sua opinião.
fonte
Como é isso? Usando um acessador particular. Só permite que você obtenha as variáveis, embora não as defina, depende do caso de uso.
fonte
Eu tenho uma solução, mas não tenho certeza se é sem falhas.
Para que funcione, você deve usar a seguinte estrutura:
Aqui está o código:
Como isso funciona é que ela fornece uma função de instância "this.getPrivateFields" para acessar o objeto de variáveis privadas "privateFields", mas essa função retornará apenas o objeto "privateFields" dentro do fechamento principal definido (também funções de protótipo usando "this.getPrivateFields "precisa ser definido dentro deste fechamento).
Um hash produzido durante o tempo de execução e difícil de ser adivinhado é usado como parâmetros para garantir que, mesmo que "getPrivateFields" seja chamado fora do escopo do fechamento, não retorne o objeto "privateFields".
A desvantagem é que não podemos estender o TestClass com mais funções de protótipo fora do fechamento.
Aqui está um código de teste:
EDIT: Usando este método, também é possível "definir" funções privadas.
fonte
Estava brincando com isso hoje e essa foi a única solução que pude encontrar sem usar o Symbols. O melhor de tudo é que tudo pode ser completamente privado.
A solução é baseada em um carregador de módulo caseiro, que basicamente se torna o mediador de um cache de armazenamento privado (usando um mapa fraco).
fonte
Sei que já faz mais de uma década que isso foi perguntado, mas acabei de pensar nisso pela enésima vez na vida de programador e encontrei uma possível solução que ainda não sei se ainda gosto totalmente. . Eu não vi essa metodologia documentada antes, então vou chamá-la de "padrão de dólar público / privado" ou _ $ / $ .
O conceito usa uma função ClassDefinition que retorna uma função Constructor que retorna um objeto Interface . O único método da interface é o
$
que recebe umname
argumento para chamar a função correspondente no objeto construtor, quaisquer argumentos adicionais passados apósname
são passados na invocação.A função auxiliar definida globalmente
ClassValues
armazena todos os campos em um objeto, conforme necessário. Ele define a_$
função para acessá-losname
. Isso segue um padrão curto de obtenção / configuração, portanto, sevalue
for passado, ele será usado como o novo valor da variável.A função definida globalmente
Interface
pega um objeto e umValues
objeto para retornar um_interface
com uma única função$
que examinaobj
para encontrar uma função nomeada após o parâmetroname
e a invocavalues
como o objeto com escopo definido . Os argumentos adicionais passados para$
serão passados na chamada de função.Na amostra abaixo,
ClassX
é atribuído o resultado deClassDefinition
, qual é aConstructor
função.Constructor
pode receber qualquer número de argumentos.Interface
é o que o código externo obtém após chamar o construtor.Não faz sentido ter funções não prototipadas
Constructor
, embora você possa defini-las no corpo da função construtora. Todas as funções são chamadas com o padrão de dólar públicothis.$("functionName"[, param1[, param2 ...]])
. Os valores privados são acessados com o padrão de dólar privadothis._$("valueName"[, replacingValue]);
. ComoInterface
não existe uma definição para_$
, os valores não podem ser acessados por objetos externos. Como o corpo de cada função prototipadathis
é definido como ovalues
objeto em função$
, você receberá exceções se chamar diretamente as funções irmãos do Constructor; o padrão _ $ / $ também precisa ser seguido nos corpos das funções prototipadas. Abaixo o uso da amostra.E a saída do console.
O padrão _ $ / $ permite total privacidade de valores em classes totalmente prototipadas. Não sei se vou usar isso, nem se tem falhas, mas, ei, foi um bom quebra-cabeça!
fonte
ES6 WeakMaps
Usando um padrão simples baseado no ES6, o WeakMaps é possível obter variáveis de membro privadas, acessíveis a partir das funções do protótipo .
Uma explicação mais detalhada desse padrão pode ser encontrada aqui
fonte
Você precisa alterar três itens no seu código:
var privateField = "hello"
porthis.privateField = "hello"
.privateField
porthis.privateField
.privateField
porthis.privateField
.O código final seria o seguinte:
fonte
this.privateField
não seria um campo privado. é acessível a partir do exterior:t.privateField
Você pode usar uma atribuição de protótipo na definição do construtor.
A variável estará visível para o método adicionado protótipo, mas todas as instâncias das funções acessarão a mesma variável SHARED.
Espero que isso possa ser útil.
fonte