Métodos privados JavaScript

482

Para criar uma classe JavaScript com um método público, eu faria algo como:

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

Dessa forma, os usuários da minha classe podem:

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

Como crio um método privado que pode ser chamado pelos métodos buy_foode use_restroom, mas não externamente pelos usuários da classe?

Em outras palavras, quero que minha implementação de método seja capaz de:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

Mas isso não deve funcionar:

var r = new Restaurant();
r.private_stuff();

Como eu defino private_stuffcomo um método privado para que ambos sejam verdadeiros?

Eu li o artigo de Doug Crockford algumas vezes, mas não parece que métodos "privados" possam ser chamados por métodos públicos e métodos "privilegiados" podem ser chamados externamente.

Wayne Kao
fonte

Respostas:

403

Você pode fazer isso, mas a desvantagem é que ele não pode fazer parte do protótipo:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}
17 de 26
fonte
9
Esconder a parte interna do fechamento não garante privacidade a todos os intérpretes. Veja code.google.com/p/google-caja/wiki/…
Mike Samuel
51
@mikesamuel - verdade, mas apenas quando esses intérpretes têm erros neles :)
jvenema
133
Esse é um método particular, mas tenderá a consumir muito mais memória do que o método de protótipo usual, especialmente se você estiver criando muitos desses objetos. Para cada instância do objeto, ele cria uma função separada vinculada ao objeto e não à classe. Além disso, isso não coleta lixo até que o próprio objeto seja destruído.
Arindam
4
Se você criar um objeto que o McDonalds () herda de Restaurant (), o McDonalds não poderá substituir os métodos privados se você os declarar dessa maneira. Bem, na verdade você pode, mas não funcionará se algum outro método chamar particular, chamará a versão original do método e você também não poderá chamar o método pai. Até o momento, não encontrei uma boa maneira de declarar métodos privados que funcionam bem com herança. Isso e as implicações de desempenho tornam esse um padrão de design não muito bom. Eu recomendaria fazer algum tipo de prefixo para indicar métodos privados, começando com um sublinhado.
Hoffmann
68
Métodos privados não devem ser substituídos - eles são privados.
17 de 26
163

Usando a função de auto-chamada e chamada

O JavaScript usa protótipos e não possui classes (ou métodos), como linguagens orientadas a objetos. Um desenvolvedor de JavaScript precisa pensar em JavaScript.

Citação da Wikipedia:

Ao contrário de muitas linguagens orientadas a objetos, não há distinção entre uma definição de função e uma definição de método. Em vez disso, a distinção ocorre durante a chamada de função; quando uma função é chamada como método de um objeto, a palavra-chave local da função é vinculada a esse objeto para essa chamada.

Solução usando uma função de auto-chamada e a função de chamada para chamar o "método" privado:

var MyObject = (function () {

    // Constructor
    function MyObject (foo) {
        this._foo = foo;
    }

    function privateFun (prefix) {
        return prefix + this._foo;
    }

    MyObject.prototype.publicFun = function () {
        return privateFun.call(this, '>>');
    }

    return MyObject;
})();


var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined

A função de chamada nos permite chamar a função privada com o contexto apropriado ( this).


Mais simples com o Node.js

Se você estiver usando o node.js , não precisará do IIFE, pois poderá aproveitar o sistema de carregamento do módulo :

function MyObject (foo) {
    this._foo = foo;
}

function privateFun (prefix) {
    return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
    return privateFun.call(this, '>>');
}

exports.MyObject = MyObject;

Carregue o arquivo:

var MyObject = require('./MyObject').MyObject;

var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined


(experimental) ES7 com o operador Bind

O operador de ligação ::é uma proposta ECMAScript e é implementada no Babel ( estágio 0 ).

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun('>>');
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}

Carregue o arquivo:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function
Yves M.
fonte
34
Esta é a melhor resposta. Os benefícios de métodos privados, sem lixo.
precisa
1
@TaylorMac Exceto pela .callpeça.
pishpish
1
@janje Huh? Esse é o ponto da questão, não há privado f()em javascript (não no momento).
Yves M.
2
@YvesM. O objetivo da pergunta é escolher o melhor padrão que simula.
pishpish
1
@ alterou qual é o melhor compromisso de ter funções ocultas (impossível de chamar de fora), boa sintaxe (não .call) e pouca memória (nenhuma função na instância)? Isso existe?
Ciprian Tomoiagă
161

Você pode simular métodos privados como este:

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

Mais informações sobre esta técnica aqui: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html

georgebrock
fonte
7
Eu também sugeriria o site de Douglas Crockford como um recurso em métodos / membros públicos / privados javascript.crockford.com/private.html
Jared
10
Ele mencionou esse link na pergunta.
Gulzar Nazim
8
A desvantagem desse método é que você não pode acessar private_stuff () outros dados privados em Restaurant e outros métodos Restaurant não podem chamar private_stuff (). A vantagem é que, se você não precisar de nenhuma das condições que acabei de mencionar, você pode manter use_restroom () no protótipo.
17 de 26
6
Essa deve ser a solução e a resposta aceita, porque o autor está usando claramente a propriedade prototype.
Gabriel Llamas
23
Com o padrão proposto por @georgebrock, todos os dados privados serão compartilhados entre todos os objetos de restaurante. Isso é semelhante a variáveis ​​privadas estáticas e funções no OOP baseado em classe. Presumo que o OP não queira isso. Para ilustrar o que quero dizer, criei um jsFiddle .
feklee
35

Nessas situações, quando você possui uma API pública e deseja métodos / propriedades públicas e privadas, sempre uso o Padrão do Módulo. Esse padrão foi popularizado na biblioteca YUI e os detalhes podem ser encontrados aqui:

http://yuiblog.com/blog/2007/06/12/module-pattern/

É realmente simples e fácil para outros desenvolvedores compreenderem. Para um exemplo simples:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay

fonte
esse tipo de coisa não seria detectado pelo preenchimento automático de um IDE :(
Clique em Upvote
19
Mas como essa não é uma classe, não é possível ter duas "instâncias" com estados diferentes.
precisa saber é o seguinte
remova a parte () e você terá uma "classe". pelo menos onde você pode instanciar diferentes instâncias com diferentes estados. o padrão do módulo é bastante intensivo em memória ...
oligofren 17/02
@DevAntoine Veja os comentários da resposta de 17 de 26. No JavaScript, classes extensíveis e métodos privados não andam facilmente de mãos dadas. Minha sugestão neste caso seria ir com composição sobre herança. Crie um protótipo extensível com os mesmos métodos que o objeto concreto fechado. Depois, interno ao seu protótipo, você pode decidir quando chamar métodos no seu objeto concreto.
Existe alguma desvantagem em chamar as variáveis ​​públicas das funções privadas da seguinte forma aPrivateMethod = function() { MYLIB.aPublicProperty}:?
217 Hanna
21

Aqui está a classe que eu criei para entender o que Douglas Crockford sugeriu em seu site Membros Particulares em JavaScript

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}
Sarath
fonte
5
O que = este é um padrão que é bastante comum, popularizado por Crockford acima mencionado em seu livro "Javascript: As partes boas"
oligofren
8
thaté usado em vez de thisevitar problemas de escopo, quando funções são vinculadas a um objeto diferente. Aqui, você está armazenando thisem thate nunca usá-lo novamente, o que é o mesmo que não fazê-lo em tudo. Você deve mudar thiscom thattoda as Constructorfunções internas (não declaração de métodos). Se employeefor applyed ou called de alguma forma, esses métodos podem ser lançados, pois thisserão incorretamente vinculados.
Maroshii
Além disso, toda instância terá uma cópia completa das funções privadas, ineficiente. Além do fato de que os métodos públicos não podem acessar os vars de classe privada, me faz querer mudar para o dardo. Infelizmente angulardart é super beta.
Ray Foss
Onde está o método "construtor" nisso? Onde eu colocaria a lógica que normalmente seria executada no método construtor de uma classe quando ela é instanciada?
BadHorsie 9/03/2015
13

Eu conjurei isso: EDIT: Na verdade, alguém se vinculou a uma solução idêntica. Duh!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'

fonte
1
Essa é uma boa técnica, mas como você permitiria parâmetros em seu construtor? Por exemplo, var getAwayVehicle = new Car(100);onde 100está a velocidade e você deseja alertar a velocidade. Obrigado!
Jason
1
Descobri isso, pode ter var Car = function(speed) { this.speed = speed; }e `return {constructor: Car, ...`
Jason
11

Penso que essas questões surgem repetidas vezes devido à falta de entendimento dos fechamentos. Сlosures é a coisa mais importante em JS. Todo programador de JS precisa sentir a essência disso.

1. Antes de tudo, precisamos criar um escopo separado (fechamento).

function () {

}

2. Nesta área, podemos fazer o que quisermos. E ninguém saberá sobre isso.

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3. Para que o mundo conheça nossa classe de restaurantes, precisamos devolvê-la do fechamento.

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4. No final, temos:

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5. Além disso, essa abordagem tem potencial para herança e modelagem

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

Espero que isso ajude alguém a entender melhor esse assunto

imos
fonte
2
O que você tem no ponto 4. é maravilhoso! Eu acho que é a única resposta de todas aqui, onde você obtém os ganhos de desempenho / memória do uso de métodos no protótipo E esses métodos públicos têm acesso total aos membros privados. +1
Hudon 08/10/2013
7
Isso não funciona. A variável name aqui atua como variável estática e é compartilhada por todas as instâncias do Restaurant. Aqui está o jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shital Shah
é uma boa tentativa, mas, como Shital apontou, a variável name é buggy.
Oligofren
2
adicionando minha 2c aqui para reafirmar isso não funciona, parece bom, mas como fora apontado acima "nome" servirá como uma variável estática ou seja compartilhado entre todas as instâncias
Paul Carroll
10

Pessoalmente, prefiro o seguinte padrão para criar classes em JavaScript:

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

Como você pode ver, ele permite definir propriedades de classe e propriedades de instância, cada uma das quais pode ser pública e privada.


Demo

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<ul>' 
               + '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
               + '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
               + '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
               + '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
               + '</ul>';
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + '<h3>Overview</h3>'
              + '<ul>' 
              + '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
              + '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
              + '</ul>'
              + '<hr />';
        }
    };
}();

var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");

Restaurant.printStatus();

Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();

Restaurant.printStatus();

BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();

Restaurant.printStatus();

Veja também este violino .

John Slegers
fonte
Isto faz-me querer usar ES6 aulas e ver o que transpiles também
sheriffderek
9

Todo esse fechamento custará você. Certifique-se de testar as implicações de velocidade, especialmente no IE. Você descobrirá que está melhor com uma convenção de nomenclatura. Ainda existem muitos usuários corporativos da Web que são forçados a usar o IE6 ...

Kelly
fonte
34
Quem se importa, sério?
nowayyy
17
Os 9% que ainda usam o IE6 não se importam com velocidade, otimizações e todos os recursos modernos do HTML5. Portanto, o fechamento não custa nada.
Gabriel Llamas
6
Agora é de 0,5% (agosto de 2012) w3schools.com/browsers/browsers_explorer.asp
Lorenzo Polidori
7
@LorenzoPolidori usuários do w3schools! == usuários corporativos da web;]
WynandB
Uma convenção de nomenclatura (por exemplo: anexar um sublinhado) é o caminho a seguir. O código é mais fácil de manter e os métodos ainda estão definidos no protótipo. Hoje em dia ... apenas marquei o método como privado em texto datilografado.
David Sherret 10/02
5

Não seja tão detalhado. É Javascript. Use uma convenção de nomenclatura .

Após anos trabalhando nas classes es6, comecei recentemente a trabalhar em um projeto es5 (usando o requireJS, que já é muito detalhado). Estive repetidamente todas as estratégias mencionadas aqui e tudo basicamente se resume a usar uma convenção de nomenclatura :

  1. Javascript não possui palavras-chave de escopo private. Outros desenvolvedores que inserem Javascript saberão disso com antecedência. Portanto, uma simples convenção de nomenclatura é mais que suficiente. Uma convenção de nomenclatura simples de prefixo com um sublinhado resolve o problema das propriedades particulares e dos métodos privados.
  2. Vamos aproveitar o Prototype por motivos de velocidade, mas não vamos ficar mais detalhados do que isso. Vamos tentar manter a "classe" es5 parecida com o que podemos esperar em outras linguagens de back-end (e tratar todos os arquivos como uma classe, mesmo que não precisemos retornar uma instância).
  3. Vamos demonstrar com uma situação de módulo mais realista (usaremos es5 e requireJs antigos).

my-tooltip.js

    define([
        'tooltip'
    ],
    function(
        tooltip
    ){

        function MyTooltip() {
            // Later, if needed, we can remove the underscore on some
            // of these (make public) and allow clients of our class
            // to set them.
            this._selector = "#my-tooltip"
            this._template = 'Hello from inside my tooltip!';
            this._initTooltip();
        }

        MyTooltip.prototype = {
            constructor: MyTooltip,

            _initTooltip: function () {
                new tooltip.tooltip(this._selector, {
                    content: this._template,
                    closeOnClick: true,
                    closeButton: true
                });
            }
        }

        return {
            init: function init() {
               new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
            }

            // You could instead return a new instantiation, 
            // if later you do more with this class.
            /* 
            create: function create() {
               return new MyTooltip();
            }
            */
        }
    });
programador
fonte
2
Deve-se observar que nem a linguagem Javascript nem qualquer host típico do navegador define objetos que dependem de convenções de nomenclatura para "ocultar" o estado privado; portanto, embora você possa estar certo de que os desenvolvedores entenderão o conceito, isso ainda leva a Abordagem OO para programação OO.
rica Remer
Posso pedir uma boa referência para fazer isso? Há partes neste exemplo que são novas para mim. O define, constructoree a própria estrutura. Embora eu concorde principalmente com a resposta, comecei a trabalhar com JS com muita influência no POO e até pulei muito cedo para o TS, pois tinha experiência anterior em C #. Acho que tenho que desaprender essas coisas e entender o paradigma de prototipagem / procedimento. (votado, btw)
Cold Cerberus
1
@ColdCerberus este trecho de código está usando es5. Você pode ver uma imagem completa dessa abordagem aqui: gist.github.com/jonnyreeves/2474026 . Mas lembre-se de que você deve adotar essa abordagem e atualizá-la para usar os módulos es6: googlechrome.github.io/samples/classes-es6 e es6 (sintaxe de importação / exportação): hackernoon.com/…
prograhammer
5

Você pode fazer isso agora com métodos privados es10 . Você só precisa adicionar um #antes do nome do método.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}
D-Marc
fonte
2
Exceto que este é o estágio 3 e ainda não faz parte oficialmente do idioma.
misterhtmlcss
3

Pegue qualquer uma das soluções que seguem o padrão privado ou privilegiado de Crockford . Por exemplo:

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

Nos casos em que o invasor não tem direito de "execução" no contexto JS, ele não tem como acessar campos ou métodos "públicos" ou "privados". Caso o invasor tenha esse acesso, ele pode executar este procedimento:

eval("Foo = " + Foo.toString().replace(
    /{/, "{ this.eval = function(code) { return eval(code); }; "
));

Observe que o código acima é genérico para toda a privacidade do tipo construtor. Falhará com algumas das soluções aqui, mas deve ficar claro que praticamente todas as soluções baseadas em fechamento podem ser quebradas dessa maneira com replace()parâmetros diferentes .

Após isso ser executado, qualquer objeto criado new Foo()terá um evalmétodo que pode ser chamado para retornar ou alterar valores ou métodos definidos no fechamento do construtor, por exemplo:

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

O único problema que vejo com isso é que ele não funcionará nos casos em que há apenas uma instância e é criada no carregamento. Porém, não há razão para definir um protótipo e, nesse caso, o atacante pode simplesmente recriar o objeto em vez do construtor, desde que ele possua uma maneira de transmitir os mesmos parâmetros (por exemplo, eles são constantes ou calculados a partir dos valores disponíveis).

Na minha opinião, isso praticamente torna a solução de Crockford inútil. Como a "privacidade" é facilmente quebrada, as desvantagens de sua solução (legibilidade e manutenção reduzidas, desempenho reduzido, aumento de memória) tornam o método baseado em protótipo "sem privacidade" a melhor escolha.

Eu costumo usar sublinhados principais para marcar __privatee _protectedmétodos e campos (estilo Perl), mas a idéia de ter privacidade em JavaScript mostra apenas como é uma linguagem incompreendida.

Portanto, eu discordo de Crockford, exceto por sua primeira frase.

Então, como você obtém privacidade real em JS? Coloque tudo o que é necessário para ser privado no lado do servidor e use o JS para fazer chamadas AJAX.

Fozi
fonte
Esse é um problema sério que deve ser mais conhecido. Existe uma 'defesa' contra esse ataque?
James
@ James Nenhum que eu saiba, acho que é a natureza da besta. Como indiquei, você pode mover a funcionalidade para o servidor em que é executado em um ambiente protegido. O ponto que eu queria superar em minha resposta, porém, é que a solução de Crockford não está ajudando, complica desnecessariamente o código e oculta a necessidade de fazer algo a respeito.
Fozi 03/02
Se um usuário digitar uma senha secreta, ele não poderá fazer isso no lado do servidor. Em algum momento, a senha estará em um var 'privado'. Então, um invasor poderia lê-lo? Confio no meu código e, de qualquer maneira, os padrões da minha casa não permitem eval (). O invasor pode ser algum plug-in ou biblioteca JavaScript mal-intencionada de terceiros que eu não verifiquei corretamente - então, sim, preciso verificar isso. O invasor também pode ser algo como um anúncio do lado que não deve interagir com o meu código. Eu me protejo contra isso, envolvendo todo o meu código em um anônimo (function () {allMyStuff}());para não expor nada global.
James
@ James Isso está chegando ao AT, se você quiser continuar, abra uma nova pergunta. Sim, um invasor pode ler a senha. Da sua variável "privada". Ou do DOM. Ou ele pode substituir a API AJAX. Ou ele substitui sua página por outra coisa. Se ele não puder executar nenhuma das opções acima, não há necessidade de "privacidade" do JS, porque ele também não pode ler nenhuma das suas variáveis ​​JS. O ponto é que a "solução" de Crockford que todo mundo está usando no momento não está ajudando com esse problema.
Fozi 04/04
Acredito que a ofuscação de código pseudo-aleatório possa ser uma defesa fraca contra esse ataque - mais difícil de modificar o corpo da função quando você não pode depender da função ter um nome fixo; mais difícil de fazer f.eval('nameOfVariable')quando você não sabe o que 'nameOfVariable'é ...
Gershom
2

A apoteose do padrão do módulo: o padrão revelador do módulo

Uma pequena extensão elegante para um padrão muito robusto.

Daniel Stockman
fonte
2

Se você deseja que toda a gama de funções públicas e privadas com a capacidade de funções públicas acessem funções privadas, código de layout para um objeto como este:

function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as 
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}
domgblackwell
fonte
Alguém pode me dizer por que isso foi rejeitado? Parece bom para mim.
thomasrutter
10
Cada vez que você faz a new MyObject, o protótipo de MyObjecté substituído pelos mesmos valores.
bpierre
2
-1. Nunca, nunca atribua ao .prototypeinterior do construtor.
Bergi 07/08/19
2
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert( "privateMethod, " + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert( "publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod || "no privateMethod" );

Semelhante ao georgebrock, mas um pouco menos detalhado (IMHO) Algum problema em fazê-lo dessa maneira? (Eu não vi isso em lugar nenhum)

edit: eu percebi que isso é meio inútil, já que toda instanciação independente tem sua própria cópia dos métodos públicos, prejudicando o uso do protótipo.

David
fonte
2

Aqui está o que eu mais gostei até agora em relação a métodos / membros privados / públicos e instanciação em javascript:

aqui está o artigo: http://www.sefol.com/?p=1090

e aqui está o exemplo:

var Person = (function () {

    //Immediately returns an anonymous function which builds our modules 
    return function (name, location) {

        alert("createPerson called with " + name);

        var localPrivateVar = name;

        var localPublicVar = "A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called ")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");

//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");

//Prints "ben"
x.publicFunction();

//Prints "candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints "candide"
y.publicFunction();

//Should have changed this : prints "Ben 2"
x.publicFunction();

JSFiddle: http://jsfiddle.net/northkildonan/kopj3dt3/1/

low_rents
fonte
essa abordagem tem uma preocupação importante - se você criou 2 objetos, na memória haverá 2 mesmos métodos (PublicFunction, por exemplo). 1000 objetos consumirão toda a sua memória.
Artem G
2

O padrão do módulo está correto na maioria dos casos. Mas se você tiver milhares de instâncias, as classes economizam memória. Se economizar memória é uma preocupação e seus objetos contêm uma pequena quantidade de dados privados, mas possuem muitas funções públicas, convém que todas as funções públicas residam no protótipo. Para economizar memória.

Isto é o que eu vim com:

var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

O objeto privcontém propriedades particulares. É acessível através da função pública getPriv(), mas essa função retorna, a falsemenos que você a passe secret, e isso é conhecido apenas dentro do fechamento principal.

James
fonte
Isso simula membros protegidos, os tipos herdados também podem acessar os membros protegidos. Sou favorável a esse padrão sobre o privado, bem
HMR
2

Que tal isso?

var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

A pesquisa de variáveis ​​privadas é impossível fora do escopo da função imediata. Não há duplicação de funções, economizando memória.

A desvantagem é que a pesquisa de variáveis ​​privadas é desajeitada privateVars[this.id].cookede ridícula ao digitar. Há também uma variável "id" extra.

Evan Leis
fonte
Isso sairá Restaurantcomo undefinedporque você não está retornando nada da função anônima.
usuário4815162342
Onde e como? Supondo que a referência ao restaurante criado seja perdida, privateVars não terá uma referência ao seu proprietário. O gráfico de referência é acíclico. o que estou perdendo?
Evan Leis
Na verdade, esta é a única resposta que suporta propriedades privadas além dos métodos. As únicas duas questões já estão anotadas na resposta.
pishpish
Eu vejo um vazamento de memória: quando uma instância de Restaurantfoi coletada no lixo, seus valores permanecem dentro privateVars. A WeakMappode ser um bom substituto para o Arrayneste caso.
Gershom
2

Agrupe todo o código na Função Anônima: Então, todas as funções serão privadas, APENAS funções anexadas ao windowobjeto:

(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;   
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

Usa isto :

  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

FIDDLE

Abdennour TOUMI
fonte
1

Eu prefiro armazenar dados privados em um associado WeakMap. Isso permite que você mantenha seus métodos públicos no protótipo ao qual eles pertencem. Essa parece ser a maneira mais eficiente de lidar com esse problema para um grande número de objetos.

const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};
remer rico
fonte
0

Funções privadas não podem acessar as variáveis ​​públicas usando o padrão do módulo

Dooma
fonte
0

Como todo mundo estava postando aqui seu próprio código, eu também vou fazer isso ...

Eu gosto de Crockford porque ele introduziu padrões reais de orientação a objetos em Javascript. Mas ele também apresentou um novo mal-entendido, o "aquele".

Então, por que ele está usando "isso = isso"? Não tem nada a ver com funções privadas. Tem a ver com funções internas!

Porque, de acordo com Crockford, este é um código de buggy:

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

Então ele sugeriu fazer isso:

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

Então, como eu disse, tenho certeza de que Crockford estava errado na explicação sobre isso e isso (mas seu código está certamente correto). Ou ele estava apenas enganando o mundo Javascript, para saber quem está copiando seu código? Não sei ... Não sou nerd de navegadores; D

EDITAR

Ah, é disso que se trata: O que significa 'var that = this;' significa em JavaScript?

Então, Crockie estava realmente errado com a explicação dele ... mas certo com o código dele, então ele ainda é um cara legal. :))

marca
fonte
0

Em geral, adicionei o objeto privado _ temporariamente ao objeto. Você precisa abrir a privacidade de forma explícita no "Construtor de poder" para o método Se você chamar o método a partir do protótipo, poderá sobrescrever o método do protótipo

  • Torne um método público acessível no "Construtor de energia": (ctx é o contexto do objeto)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • Agora eu tenho esse openPrivacy:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };
Andreas Dyballa
fonte
0

Isto é o que eu descobri:

Precisa de uma classe de código de açúcar que você pode encontrar aqui . Também suporta coisas protegidas, herança, virtuais e estáticas ...

;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace[ "Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace, "Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log( "There are", this.toilets, "toilets available") }

    function buy_food     (){ return "food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

fonte
0
Class({  
    Namespace:ABC,  
    Name:"ClassL2",  
    Bases:[ABC.ClassTop],  
    Private:{  
        m_var:2  
    },  
    Protected:{  
        proval:2,  
        fight:Property(function(){  
            this.m_var--;  
            console.log("ClassL2::fight (m_var)" +this.m_var);  
        },[Property.Type.Virtual])  
    },  
    Public:{  
        Fight:function(){  
            console.log("ClassL2::Fight (m_var)"+this.m_var);  
            this.fight();  
        }  
    }  
});  

https://github.com/nooning/JSClass

snowkid
fonte
0

Criei uma nova ferramenta para permitir que você tenha métodos privados verdadeiros no protótipo https://github.com/TremayneChrist/ProtectJS

Exemplo:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail
Trem
fonte
1
Você pode explicar como funciona, por favor? Como / onde você pode chamar o _privatemétodo?
Bergi 7/08/2014
0

Você precisa encerrar sua função construtora real, onde pode definir seus métodos particulares. Para alterar os dados das instâncias por meio desses métodos particulares, é necessário dar a eles "isso" com eles, como argumento de função ou chamando essa função com .apply (this):

var Restaurant = (function(){
    var private_buy_food = function(that){
        that.data.soldFood = true;
    }
    var private_take_a_shit = function(){
        this.data.isdirty = true;   
    }
    // New Closure
    function restaurant()
    {
        this.data = {
            isdirty : false,
            soldFood: false,
        };
    }

    restaurant.prototype.buy_food = function()
    {
       private_buy_food(this);
    }
    restaurant.prototype.use_restroom = function()
    {
       private_take_a_shit.call(this);
    }
    return restaurant;
})()

// TEST:

var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.log(McDonalds);
console.log(McDonalds.__proto__);
Flex Elektro Deimling
fonte
Na verdade, não funciona. Todos new Restaurantterão seu próprio restaurantconstrutor, e o "protótipo" é totalmente abusado.
Bergi 7/08/2014
@Bergi. Na verdade, você está certo. Funcionaria, mas também seria um porco de recursos (é assim chamado?). Eu editei minha resposta em relação a isso.
Flex Elektro Deimling
Obrigado pela atualização. Nenhuma idéia que chamar a versão anterior (mas "bug" :-)
Bergi
0

Eu sei que é um pouco tarde demais, mas e quanto a isso?

var obj = function(){
    var pr = "private";
    var prt = Object.getPrototypeOf(this);
    if(!prt.hasOwnProperty("showPrivate")){
        prt.showPrivate = function(){
            console.log(pr);
        }
    }    
}

var i = new obj();
i.showPrivate();
console.log(i.hasOwnProperty("pr"));
Maxim Balaganskiy
fonte
0

Já existem muitas respostas para essa pergunta, mas nada atendeu às minhas necessidades. Então, eu vim com minha própria solução, espero que seja útil para alguém:

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    function getClass(line){
        var i = line.indexOf(" ");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing

Como você pode ver, este sistema funciona ao usar esse tipo de classe em javascript. Tanto quanto eu descobri, nenhum dos métodos comentados acima o fez.

thegunmaster
fonte
1
Curioso: a sua necessidade era realmente expor a função, mas torná-la não operacional no tempo de execução - em vez de ocultá-la de chamadores externos, como todas / a maioria das outras respostas fazem? Se sim, por quê? O que você considera ser o benefício dessa abordagem? Para mim, isso parece ser apenas uma sobrecarga desnecessária desempenho, uma API claro e, bem, provavelmente obrigado a causar depuração inferno, mas estou sempre aberto a novas perspectivas ...
JHH
2
@JHH, para ser sincero, eu estou com o rosto pálido ao olhar para trás. Geralmente, as despesas gerais não valem a pena, embora, para mim, não importasse muito, pois eu não fazia muitas chamadas para essas classes. A razão pela qual eu fiz dessa maneira é que ela é relativamente limpa na maneira como você escreve e chama as funções. Eu não entendia símbolos e coisas assim, mas agora que entendo, acho que geralmente é o caminho a percorrer ao usar as aulas. Estou pensando em remover esta resposta todos juntos. Eu publiquei várias respostas estúpidas, mas ei, você vive e aprende.
Thegunmaster
Obrigado pelo feedback! Eu não tinha certeza se havia entendido algo errado. Mas sim, todos nós vivemos e aprendemos!
JHH
0

Consulte esta resposta para obter uma solução 'classe' limpa e simples com uma interface pública e privada e suporte para composição

kofifus
fonte