Métodos privados de JavaScript
Para crear una clase de JavaScript con un método público, haría algo como:
function Restaurant() {}
Restaurant.prototype.buy_food = function(){
// something here
}
Restaurant.prototype.use_restroom = function(){
// something here
}
De esa manera los usuarios de mi clase pueden:
var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();
¿Cómo creo un método privado que pueda ser llamado por buy_food
yuse_restroom
pueden llamar pero no externamente los usuarios de la clase?
En otras palabras, quiero que la implementación de mi método pueda hacer:
Restaurant.prototype.use_restroom = function() {
this.private_stuff();
}
Pero esto no debería funcionar:
var r = new Restaurant();
r.private_stuff();
¿Cómo defino?private_stuff
como un método privado para que ambos sean válidos?
He leído el artículo de Doug Crockford varias veces, pero no parece que los métodos públicos puedan llamar a métodos "privados" y que los métodos "privilegiados" puedan llamarse externamente.
Puedes hacerlo, pero la desventaja es que no puede ser parte del prototipo:
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();
}
}
Usando la función de autoinvocación y la llamada
JavaScript utiliza prototipos y no tiene clases (o métodos) como los lenguajes orientados a objetos. Un desarrollador de JavaScript necesita pensar en JavaScript.
Cita de Wikipedia:
A diferencia de muchos lenguajes orientados a objetos, no existe distinción entre una definición de función y una definición de método. Más bien, la distinción ocurre durante la llamada a la función; cuando se llama a una función como método de un objeto, la palabra clave local this de la función está vinculada a ese objeto para esa invocación.
Solución utilizando una función de autoinvocación y la función de llamada para llamar al "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
La función de llamada nos permite llamar a la función privada con el contexto apropiado ( this
).
Más sencillo con Node.js
Si está utilizando Node.js , no necesita IIFE porque puede aprovechar el sistema de carga de módulos :
function MyObject(foo) {
this._foo = foo;
}
function privateFun(prefix) {
return prefix + this._foo;
}
MyObject.prototype.publicFun = function () {
return privateFun.call(this, ">>");
}
module.exports= MyObject;
Cargue el archivo:
var MyObject = require("./MyObject");
var myObject = new MyObject("bar");
myObject.publicFun(); // Returns ">>bar"
myObject.privateFun(">>"); // ReferenceError: private is not defined
(¡nuevo!) Métodos privados nativos en futuras versiones de JavaScript
La propuesta de métodos privados y captadores/definidores para clases de JavaScript de TC39 se encuentra en la etapa 3. ¡Eso significa que en el corto plazo JavaScript implementará métodos privados de forma nativa!
Tenga en cuenta que los campos de clase privada de JavaScript ya existen en las versiones modernas de JavaScript.
A continuación se muestra un ejemplo de cómo se utiliza:
class MyObject {
// Private field
#foo;
constructor(foo) {
this.#foo = foo;
}
#privateFun(prefix) {
return prefix + this.#foo;
}
publicFun() {
return this.#privateFun(">>");
}
}
Es posible que necesite un transpilador/compilador de JavaScript para ejecutar este código en motores JavaScript antiguos.
PD: Si te preguntas por qué el #
prefijo, lee esto .
(obsoleto) ES7 con el operador Bind
Advertencia: la propuesta del operador de enlace TC39 está casi muerta https://github.com/tc39/proposal-bind-operator/issues/53#issuecomment -374271822
El operador de enlace ::
es una propuesta de ECMAScript y está implementado en Babel ( etapa 0 ).
export default class MyObject {
constructor (foo) {
this._foo = foo;
}
publicFun () {
return this::privateFun(">>");
}
}
function privateFun (prefix) {
return prefix + this._foo;
}
Puedes 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();
Más información sobre esta técnica aquí: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html
En estas situaciones, cuando tienes una API pública y quieres métodos/propiedades públicas y privadas, siempre uso el patrón de módulo. Este patrón se hizo popular en la biblioteca YUI y los detalles se pueden encontrar aquí:
http://yuiblog.com/blog/2007/06/12/module-pattern/
Es realmente sencillo y fácil de comprender para otros desarrolladores. Para un ejemplo sencillo:
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
Métodos privados ES12
Puede hacer esto ahora con los métodos privados de es12 . Solo necesita agregar un #
antes del nombre del método.
class ClassWithPrivateMethod {
#privateMethod() {
return 'hello world';
}
getPrivateMessage() {
return #privateMethod();
}
}