Métodos privados de JavaScript

Resuelto Wayne Kao asked hace 16 años • 34 respuestas

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_foodyuse_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.

Wayne Kao avatar Sep 11 '08 08:09 Wayne Kao
Aceptado

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();
    }
}
17 of 26 avatar Sep 11 '2008 01:09 17 of 26

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;
}
Yves M. avatar Aug 07 '2014 01:08 Yves M.

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

georgebrock avatar Sep 11 '2008 01:09 georgebrock

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
 avatar Sep 11 '2008 02:09

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();
  }
}
D-Marc avatar Dec 25 '2019 01:12 D-Marc