Security is an issue that should not be taken lightly. Novice programmers may be unfamiliar with some of these security techniques in JavaScript, e incluso uno que otro programmer experimentado puede que también ignore estas prácticas. Sin embargo, mas allá de la importancia de conocer estos métodos es praticarlos día a día en nuestra programming.
Project properties were traditionally left unprotected in JavaScript or hidden, captured in a closure. Symbols and WeakMaps offer another alternative.
Both Chrome (version 36) and Firefox (version 31) support WeakMaps. Chrome 36 supports Symbols but you need to enable Experimental JavaScript in chrome://flags/#enable-javascript-harmony. Firefox supports version 33 symbols.
Unprotected scenario
Instances of person created using the function below will have properties stored directly in them.
var Person = (function () {function Person (name) {this.name = name;} Person.prototype.getName = function () {return this.name;}; return Person;} ()); var p = new Person ('John'); print ('Person 1 name:' + p.getName ()); delete p.name; print ('Person 1 name:' + p.getName () + '- modified outside.');
This approach has the advantage that all instances of the Person they are similar and access to the properties of these instances can be optimized. But on the other hand there are no private properties here - all object properties can be modified by external code (in this case - deleted).
Several libraries prefer to prefix properties that are intended to be private with the underscore (for example, _Name).
Others - as TypeScript - dependen del compiler para marcar todos los usos ilegales de una propiedad privada.
Hide properties with closures
To isolate a property from an external modification, you can use an internal closure that closes over the variable name. The Douglas Crockford code conventions for JavaScript recommend this pattern when privacy is important to deter properties of names with the underscore prefix to indicate privacy.
var Person = (function () {function Person (name) {this.getName = function () {return name;};} return Person;} ()); var p = new Person ('John'); print ('Person 2 name:' + p.getName ()); delete p.name; print ('Person 2 name:' + p.getName () + 'stays private.');
The closure approach has the advantage of true privacy, but the cost is that for each instance of Person a new closure has to be created (the function within the constructor of the Person).
Use of symbols
With ES6 there is one more way to store properties: Symbols.
Symbols are similar to private names but - unlike private names - they do not provide true privacy.
Para ejecutar el ejemplo que ves tu browser debe soportar:
var Person = (function () {var nameSymbol = Symbol ('name'); function Person (name) {this [nameSymbol] = name;} Person.prototype.getName = function () {return this [nameSymbol];}; return Person;} ()); var p = new Person ('John'); print ('Person 3 name:' + p.getName ()); delete p.name; print ('Person 3 name:' + p.getName () + '- stays private.'); print ('Person 3 properties:' + Object.getOwnPropertyNames (p));
Symbols do not increase the number of closures for each instance created. There is only one clasp to protect the symbol.
Symbols are used to index JavaScript objects. The main difference with other types is that they are not converted to strings and exposed by Object.getOwnPropertyNames. Only by using the reference symbol can you set and retrieve values from the object. A list of symbols assigned to a given object can be accessed with the function Object.getOwnPropertySymbols.
Each symbol is unique, even if it was created with the same label.
ES6 Symbols ✅
var sym1 = Symbol ('a'); var sym2 = Symbol ('b'); var sym3 = Symbol ('a'); print ('sym1 === sym1:' + (sym1 === sym1)); print ('sym1 === sym2:' + (sym1 === sym2)); print ('sym1 === sym3:' + (sym1 === sym3));
Symbols have the following disadvantages:
- Greater complexity in symbol handling - instead of a simple p.name, you have to first get the symbol reference and then use p [nameSymbol].
- Actualmente sólo unos pocos browsers soportan símbolos.
- No garantizan una verdadera privacidad, pero pueden utilizarse para separar las propiedades públicas de las internas de los objetos. Es similar a cómo la mayoría de los lenguajes orientados a objetos permiten el acceso a propiedades privadas a través de la API de reflexión.
Private symbols are still considered for ECMAScript, but the proper implementation that never filters symbols is difficult. Private symbols are already used by the ES6 specification and implemented internally in V8.
Using WeakMaps
Another approach to storing private properties is WeakMaps.
A WeakMap instance is hidden within a closure and is indexed by instances of person. Map values are objects that contain private data.
var Person = (function () {var private = new WeakMap (); function Person (name) {var privateProperties = {name: name}; private.set (this, privateProperties);} Person.prototype.getName = function () {return private.get (this) .name;}; return Person;} ()); var p = new Person ('John'); print ('Person 4 name:' + p.getName ()); delete p.name; print ('Person 4 name:' + p.getName () + '- stays private.'); print ('Person 4 properties:' + Object.getOwnPropertyNames (p));
It is possible to use Map instead of a WeakMap or even a couple of arrays to mimic this solution. But using WeakMap has a significant advantage - it allows Person instances to be garbage collected.
A Map or a matrix holds objects that contain strongly. Person it is a closure that captures the private variable - which is also a strong reference. The garbage collector can collect an object if there are only weak references to it (or if there is no reference at all). Due to the two strong references, as long as the function Person be alcanzable desde las raíces de la CG, entonces cada instancia de Person jamás creada es alcanzable y por lo tanto no puede ser recolectada como basura.
The WeakMap holds the keys weakly and that makes both the Person instance and its private data eligible for garbage collection when an object Person It is no longer referenced by the rest of the application.
Access to properties of other instances
All the solutions presented (with the exception of the closures) have an interesting feature. Instances can access private properties of other instances.
The following example classifies the instances of Person by their names. The function compareTo uses the private data of this and other instances.
var Person = (function () {var private = new WeakMap (); function Person (name) {var privateProperties = {name: name}; private.set (this, privateProperties);} Person.prototype.compareTo = function (other ) {var thisName = private.get (this) .name; var otherName = private.get (other) .name; return thisName.localeCompare (otherName);}; Person.prototype.toString = function () {return private.get (this) .name;}; return Person;} ()); var people = [new Person ('John'), new Person ('Jane'), new Person ('Jim')]; people.sort (function (first, second) {return first.compareTo (second);}); print ('Sorted people:' + people.join (','));
The same example written in Java:
import java.util.Arrays; class Person implements Comparable {private String name; public Person (String name) {this.name = name; } public int compareTo (Person other) {return this.name.compareTo (other.name); } public String toString () {return this.name; }} public class Main {public static final void main (String ... args) {Person [] people = new Person [] {new Person ("John"), new Person ("Jane"), new Person ("Jim ")}; Arrays.sort (people); System.out.print ("Sorted people:" + Arrays.toString (people)); }}
The method Person :: compareTo uses the private field name of this instance and another object.
Felicidades por haber finalizado este nuevo tutorial, recuerda que en tu Web schoolJavaScript.com tendrás acceso a los mejores cursos de programming language JavaScript.