una tablas tabla obtener herencia funciones ejemplos datos con anonimas agregar javascript oop methods styles

tablas - prototype javascript ejemplos



¿Imponer métodos privados en Javascript es una buena idea? (10)

Creo que los métodos privados son buenos si quieres evitar que otros desarrolladores accedan a estos métodos incluso si saben que no deberían hacerlo.

lea este artículo de Crockford, especialmente la parte sobre OOP y miembros privados

espero que esto ayude

Estaba teniendo una discusión con un compañero desarrollador sobre si tiene sentido o no piratear las funciones privadas de JavaScript.

Las alternativas son:

  1. Un constructor y un prototipo con todas las funciones en métodos no API (privados) se nombrarán con un subrayado _function_name para que los desarrolladores sepan a qué pueden llamar y a qué no pueden llamar.
  2. Un constructor y un prototipo para funciones de API, y funciones gratuitas como funciones privadas dentro de un espacio de nombres privado que los oculta del resto de los espacios de nombres excepto este.

No consideramos otros enfoques, como la creación de funciones privadas en el constructor de la forma var private_var= function(){} porque eso activaría la creación de todas esas funciones cada vez que se crea una instancia de un objeto y cada objeto tendría su propio conjunto.

Razones que tuvimos para ellos:

1

  • Javascript no admite funciones privadas per se, de hecho, no hay concepto de visibilidad privada / protegida / pública, así que esto es básicamente un truco
  • Usar el guión bajo en los nombres de los métodos marca claramente cuáles son los límites para la clase / prototipo, no hay necesidad de "imponerlo", de hecho, los lenguajes como Python no tienen métodos privados y los usuarios de Python nunca parecían preocuparse por eso. por usar guiones bajos
  • Incluso si se hace cumplir la privacidad, ¿qué sentido tiene en un lenguaje en el que simplemente puede sustituir dinámicamente los métodos públicos?
  • Afecta a la legibilidad, bastante, las funciones privadas se separan en otro conjunto de llaves para su alcance, no pueden usar this o necesitan ser llamadas con function.call(object) o function.apply(object)

2

  • Trae una limitación clara al encapsular métodos privados lejos de las manos de usuario de clase / prototipo
  • Es más o menos el estándar de la industria, muchos desarrolladores de javascript lo usan así

Sospechamos que, debido a que muchos desarrolladores lo usan, puede haber otra razón, como el rendimiento, para usarla de esa manera.

Como nuestro conocimiento de Javascript es bastante limitado, decidimos publicar esto en stackoverflow para ver qué enfoque es mejor y por qué.


Prefiero usar un patrón de módulo revelador. Verifique aquí


Aunque esta no es realmente una respuesta a la pregunta sobre el clima, es una buena idea aplicar estrictamente los métodos privados, solo me gustaría señalar que es posible hacerlo mediante un cierre.

Básicamente es var foo=function trick pero no crea una nueva función para cada instancia del objeto. La forma de hacerlo es esta:

// Create a closure that we can share with our constructor var MyConstructor = (function(){ // Within this closure you can declare private functions // and variables: var private_function = function () {}; var private_var = {}; // Declare the actual constructor below: return function () { } })()

Personalmente, tengo dos mentes sobre esto. Viniendo de un entorno de trabajo con muchas personas trabajando en el mismo conjunto de archivos al mismo tiempo, realmente aprecio la capacidad de hacer cosas realmente privadas para que el código no se rompa por personas que intentan usar el objeto incorrectamente.

Por otro lado, viniendo de un fondo de Perl, también aprecio el hecho de que el programador original que diseñó el objeto no sea omnipotente y no pueda prever todas las formas en que se usará el código en el futuro. A veces, realmente necesitas cambiar las partes internas de un objeto en algunos casos de esquina. Esta ha sido siempre la sabiduría estándar aceptada por los programadores experimentados de Perl.

Al final, ambas formas funcionan. La mayoría del módulo en el repositorio de CPAN de Perl es muy estable y funciona de inmediato, incluso sin una privacidad real . Por otro lado, he trabajado en proyectos donde las personas han introducido errores debido a eso. Supongo que todo se reduce a cómo trabaja su equipo juntos. Si todos respetan las pautas de codificación y la cultura, el truco "_" de estilo Perl funciona muy bien. Si, por otro lado, no puede confiar en cómo los futuros mantenedores manipularán su código, entonces reforzar la privacidad más estrictamente es probablemente mejor.


Qué enfoque es mejor depende en gran medida de lo que estás tratando de lograr.

Su alternativa 1 es un acuerdo de los desarrolladores sobre una cierta convención de nomenclatura. Este enfoque es mejor siempre que tenga un equipo de desarrolladores que estén dispuestos a comprometerse con este acuerdo y que sean los únicos que utilicen el software. Como afirma correctamente, este enfoque es compatible con la legibilidad y la capacidad de prueba más importante de su código. También encontrarás muchos más desarrolladores que pueden entender y mantener el código que con la alternativa 2.

Su alternativa 2, y cualquier patrón para implementar miembros privados reales de objetos con cierres, no es un truco, sino la forma en que funciona JavaScript. Este enfoque es mejor cuando realmente necesita proteger los atributos dentro de su objeto. Este es principalmente el caso cuando la pieza de código está disponible para los desarrolladores que no son parte de su equipo o lo hacen públicamente disponible. Pero perderá algo de legibilidad y capacidad de prueba de su código y encontrará menos desarrolladores que puedan comprender y mantener el código.

También parece haber cierta confusión sobre javascript en tu lado. Tiene razón acerca de javascript no (aún) que admite atributos privados. Pero javascript tampoco admite "clases" como construcción de lenguaje y tampoco admite "métodos de clase". Todo en javascript es (solo) un objeto. El constructor de clase es solo un patrón para crear un objeto, y los métodos son (solo) atributos de objetos que son del tipo "función". Los objetos en javascript no son instancias de clases, como en los lenguajes orientados a objetos tradicionales.

Por lo tanto, cuando declare correctamente, después de la creación de un objeto en javascript, se pueden cambiar todos los atributos de ese objeto, incluidos los atributos de funciones, es decir, los métodos (excepto los atributos encapsulados en cierres). Este es el poder de JavaScript, pero en el caso de proteger objetos del cambio, es una desventaja y no algo para lo que javascript (todavía) esté diseñado. No podrá evitar que los usuarios reescriban sus objetos, pero puede hacerlo más difícil con los cierres.

Espero que esto ayude en tu decisión.


Recomendaría usar una herramienta de la próxima versión de JavaScript, ES6: WeakMap. Lo he implementado en IE8 + y he escrito sobre él y lo uso extensivamente.

function createStorage(creator){ creator = creator || Object.create.bind(null, null, {}); var map = new WeakMap; return function storage(o, v){ if (1 in arguments) { map.set(o, v); } else { v = map.get(o); if (v == null) { v = creator(o); map.set(o, v); } } return v; }; } var _ = createStorage(function(o){ return new Backing(o) }); function Backing(o){ this.facade = o; } Backing.prototype.doesStuff = function(){ return ''real value''; } function Facade(){ _(this); } Facade.prototype.doSomething = function doSomething(){ return _(this).doesStuff(); }

Más información:

  1. http://bbenvie.com/articles/2012-07-25/JavaScript-Classes-with-private-protected-and-super
  2. http://benvie.github.com/WeakMap/
  3. http://benvie.github.com/harmony-collections/

  • No es un truco : creo que es fundamental superar esta idea. Cuando utilizas una función básica legítima del lenguaje (cierres) para lograr el objetivo de ocultar los detalles de tus objetos, entonces no estás pirateando.

    Es importante tener en cuenta que cuando utiliza idiomas privados en clases, simplemente está implementando un concepto de nivel superior que es ocultar los detalles de implementación de sus objetos. Con esto en mente, no estamos tratando de imitar el modificador privado en JavaScript, simplemente estamos implementando un concepto, cómo lo implementamos no es tan relevante y depende de la herramienta / idioma.

    Por cierto, vengo de un entorno de C # y me resultó muy difícil superar esta barrera de "pirateo" cuando comencé con JavaScript, hasta hace poco, consideraba cierres, literales de objetos, funciones de constructor, funciones de devolución de llamada ... los consideré todos los hacks. Solo cuando superé esa barrera mental comencé a apreciar JavaScript y a escribir un buen código que aprovecha sus puntos fuertes.

  • En cuanto a cómo ocultar sus funciones "privadas", puede ocultarlas dentro de un espacio de nombres como sugirió, o inicializar como variables con Patrón de Módulo o una de sus variaciones que será mi preferencia. Usted dijo que esto significará que estas variables se recrearán con cada nueva instancia, pero eso es lo mismo para las variables privadas en el lenguaje basado en clases. Si está buscando un equivalente de estática, simplemente puede hacer MyClass.StaticVariable = "myStaticVariable" y así debería ser.

  • ¿Qué importancia tiene ocultar detalles en un lenguaje dinámico cuando puedes cambiar la API pública? Bueno, esto nos lleva de regreso a mi primer punto: buscar en JavaScript con ojos equivocados de lenguajes basados ​​en clases: desde el punto de vista del lenguaje dinámico, imagina toda la flexibilidad que obtienes debido a esa naturaleza dinámica. Estaba mirando la biblioteca Sinon. que proporciona maneras de burlarse de cosas como temporizadores (básicamente porque puedes secuestrar la API pública de casi todo), imagina lo difícil que sería en un lenguaje no dinámico, ver toda la necesidad de marcos de burla, contenedores de IoC, generadores de Proxy, reflexión , obtienes todo esto gratis en un lenguaje dinámico como JavaScript. Entonces, aunque su argumento es correcto desde el punto de vista técnico, cuando se lo coloca en el contexto completo, se vuelve irrelevante.

    Nuevamente, al ocultar los detalles de implementación, no lo hacemos para imitar la función C # / Java / C ++, lo hacemos porque, en primer lugar, es un buen diseño y un buen concepto de reutilización y mantenimiento, pero en segundo lugar, y en mi opinión tan importante, hazlo porque es un patrón de idioma y así es como se hace en JavaScript. No subestime eso, escribe código hoy que será leído por otra persona mañana, por lo que seguir patrones comunes (de diseño OO como en ocultación de detalles y encapsulación, y patrones específicos del idioma como en Patrón de Módulo) mejora enormemente la comunicación, que es - por sí mismo - lo suficiente como para abrazarlo en mi opinión. Si lo hace de la manera "JavaScript", entonces ya se está comunicando correctamente con los demás miembros del equipo, con el experto en JavaScript que se unirá al equipo en el futuro y también con la comunidad.

  • En lo que respecta a la legibilidad, le está haciendo un favor a los lectores de su código siguiendo patrones y convenciones comunes. Si vas y tratas de hacer lo mismo en C ++, solo entonces estás afectando la legibilidad.

  • Haga el guión bajo : las convenciones son buenas especialmente cuando se trabaja con equipos, y una de las bibliotecas más grandes jQuery UI usa esta convención y funciona y aclara mucho las cosas. Así que hágalo, incluso si sus variables no están realmente ocultas (en la interfaz de usuario de jQuery, aún puede acceder y modificar las variables subrayadas), la claridad y consistencia que aporta a su base de código es invaluable.

Por último, sigo mencionando "hazlo de la manera de JavaScript", pero esa declaración en sí misma es engañosa. Creo que hay una (s) forma (s) de JavaScript para el problema que mencionas, pero a una escala mayor, especialmente a nivel de marcos y bibliotecas, apenas hay una forma de hacer las cosas. Pero eso es parte de la emoción y la belleza de JavaScript. Abrázalo.


Hay varios enfoques para su problema, y ​​uno que elija depende de algunos factores, que incluyen (1) soporte del navegador objetivo, (2) objetivos del proyecto y (3) preferencias personales. Destacaré algunos enfoques en los que tengo opiniones en particular.

Símbolos

Destaco esto primero porque es la solución más robusta, y es el futuro de JavaScript: se incluirá en la próxima versión del estándar ECMAScript. Es posible hoy con una cuña en navegadores compatibles con ES5. Eso significa que funciona básicamente en todos los navegadores de categoría A lanzados en los últimos 2 años. La desventaja principal es que no es compatible con IE 8, pero es compatible con IE 9 y 10 (y por supuesto con todas las versiones modernas de FF, Chrome y Safari). Si IE8 aún te importa, esta no es una opción para ti todavía.

La cuña se puede descargar aquí: SymbolsForES5 . Esta biblioteca le permite comenzar a utilizar esta función, y cuando los Símbolos se incluyan de forma nativa en los navegadores en el futuro, su código podrá realizar una transición agradable.

Aquí hay un ejemplo del uso de símbolos para miembros privados:

var x = { }; var a = new Symbol(); x[a] = 5; console.log(x[a]); // => 5

Siempre que tenga acceso al objeto a Symbol, puede leer la propiedad desde el objeto x , pero si alguien que no tiene acceso a a no puede leerlo. Esto le permite realmente tener miembros privados:

var Person = (function() { var firstName = new Symbol(), lastName = new Symbol(); function Person(first, last) { this[firstName] = first; this[lastName] = last; } Person.prototype.getFullName = function() { return this[firstName] + '' '' + this[lastName]; }; return Person; })(); var john = new Person(''John'', ''Smith''); john.getFullName(); // => ''John Smith'' Object.getOwnPropertyNames(john); // => [ ]

Usar un personaje NO ACCEDER

Como mencionaste, siempre puedes prefijar una propiedad con un carácter (como un guión bajo) para indicar que no se debe acceder mediante un código externo. El principal inconveniente es que la propiedad todavía es accesible. Sin embargo, hay algunos buenos beneficios: (1) memoria eficiente (2) capacidad total para usar la herencia prototípica (3) fácil de usar (4) fácil de depurar (ya que las propiedades aparecen en un depurador) (5) funciona en todos navegadores.

He utilizado este método extensivamente en el pasado con gran éxito. Como alternativa al guión bajo, en un marco que desarrollé (joi) , utilicé el símbolo # porque dificulta el acceso a la propiedad (debe acceder a él con la notación de corchetes), lo que sirve como un recordatorio suave para cualquiera que intente para acceder a él, realmente debería ser dejado en paz:

function Person(first, last) { this[''#firstName''] = first; this[''#lastName''] = last; } Person.prototype.getFullName = function() { return this[''#firstName''] + '' '' + this[''#lastName'']; }; var john = new Person(''John'', ''Smith''); john.getFullName(); // => ''John Smith''

He usado esta técnica durante aproximadamente 7 años con gran éxito, y la recomendaría encarecidamente si los Símbolos no se ajustan a sus necesidades.

Privates Inside the Constructor

Sé que dijiste que decidiste no seguir esta técnica debido a razones de memoria / rendimiento, pero si lo que quieres es algo cercano a partes reales reales y no puedes usar Símbolos porque necesitas una compatibilidad con el navegador heredado, es una buena opción. La empresa para la que trabajo utiliza este modelo en aplicaciones a gran escala, y la memoria consumida realmente no es un problema. Además, he oído hablar de estudios que han descubierto formas de optimizar la memoria y el rendimiento en este modelo, y creo que los navegadores modernos (al menos V8 / Chrome, si no otros) están comenzando a implementar estas optimizaciones. (Esta información proviene de una charla que escuché de Brendan Eich; lo siento, no sé de qué hablar estaba fuera de mi cabeza).

Al final, hay expertos que abogan por el uso de esta técnica, y si bien no es mi preferencia, mi empresa ha tenido éxito y puedo avalarlo como un modelo confiable.

Para completar, se ve así:

function Person(first, last) { this.getFullName = function() { return first + '' '' + last; }; } var john = new Person(''John'', ''Smith''); john.getFullName(); // => ''John Smith''

Un motor de JavaScript puede optimizar esto al no crear realmente una nueva función para cada método getFullName (aunque, según el libro, se supone), ya que en este caso no hay forma de determinar si realmente crea un nuevo objeto de función cada vez. La parte engañosa es que una vez que se introduce un código que podría determinar si se crea un nuevo objeto de función cada vez, el motor debe cambiar para realmente hacer eso.

WeakMaps

En otra respuesta benvie (mencionado WeakMaps) [http://.com/a/12955077/1662998]. También consideraría esta alternativa si se necesita soporte IE8 y desea que las propiedades realmente simulen miembros privados.


Sí, es una mala idea basada en un malentendido. Comúnmente se entiende mal que los lenguajes como Java, C #, etc. tienen privacidad que se aplica o no se puede eludir. Pero en realidad eso es completamente falso y los modificadores de acceso private y de otro tipo en Java, C # etc. se reducen a asesoramiento y comunicación, no a cumplimiento.

Por Martin Fowler :

El control del punto de acceso no es para evitar el acceso, sino para indicar que la clase prefiere guardarse algunas cosas. El uso de modificadores de acceso, como muchas cosas en la programación, se trata principalmente de comunicación .

Esto es exactamente lo que hace el prefijo de subrayado. Y con el prefijo de subrayado, no está luchando contra el modelo de objetos de lenguaje.

No puede circular los cierres por ningún motivo, pero esto es posible en cualquier otro idioma. Irónicamente, todo lo que ha hecho es que ha convertido a Javascript en el lenguaje más inflexible de todos.

Como usa variables y no propiedades de objeto, no puede tener

Básicamente, las características de OOP que obtienes con los cierres se limitan a agrupar solo un conjunto de funciones. Eso solo es bueno para los ejemplos iniciales de juguetes. Y si insistes contra el sentido común de que la máquina debe imponer la privacidad, entonces ni siquiera puedes tener "paquetes privados", "protegidos" o muchos tipos de niveles más sofisticados de "control de acceso".

Por cierto, también necesitas crear objetos de función únicos (porque tienen identidad observable) para cada método por cada instancia.


¿Cómo piratearía las funciones privadas de JavaScript y con qué propósito?

Creo que los diferentes métodos para crear clases, funciones privadas, etc. dependen de tus motivaciones.

La convención de guión bajo te ayuda cuando intentas probar la unidad y realmente quieres romper lo que podría parecer un método gigante desde el exterior.

Siento que en cualquier otro caso debes tratar de hacer las cosas verdaderamente privadas. Si intentas exponer una API agradable y limpia para que otros trabajen, no deberían necesitar ver lo que está detrás de escena. ¿Por qué exponer eso? Esto lleva a convos generales sobre lo privado y lo público: ¿Por qué los métodos "privados" en el objeto orientado?

Tienes algunas opciones para privatizar métodos y algunas de estas opciones afectan el rendimiento.

Subrayar la convención:

function Pizza() { this._pepperoni = function () {}; }

o

function Pizza() { } Pizza.prototype._pepperoni = function () {};

Alcance

function Pizza() { function pepperoni() {}; }

Espaciado de nombres / Módulos

var pizza = pizza || {}; (function() { function pepperoni() {}; function create() { pepperoni(); } window.pizza.create = create; // or module.export = pizza or both }());

Patrón de módulo

(function(){ function pepperoni() {}; function Pizza() { pepperoni(); } window.Pizza = Pizza; }());

Acerca de recrear tus funciones vs definirlas una vez. Primero, si desea usar miembros privados internos y todavía usa "esto" simplemente cree una nueva variable llamada self y asígnele this :

function Pizza() { var self = this; function pep() { self.x = 1; } }

A continuación, intenté probar la diferencia de rendimiento entre redefinir y escribir funciones desde el principio : http://jsperf.com/private-methods Creo que te ahorra un poco menos del 20% en ops / sec para que tus funciones se vuelvan a crear cada hora.

No recomiendo ningún enfoque, todos son válidos y útiles en varias ocasiones. A veces se trata más de semántica, a veces se trata de rendimiento y otras veces es para encontrar un final, como pruebas unitarias.


Donde trabajo, el patrón recomendado es usar el "patrón de espacios de nombres" y tratar los espacios de nombres como lo haría en cualquier lenguaje OO.

Puedes ver cómo en esta respuesta