Skip to main content

Security is an issue that should not be taken lightly. Novice programmers may be unfamiliar with some of these JavaScript security techniques, and even an experienced programmer may be ignorant of these practices as well. However, beyond the importance of knowing these methods is to practice them day by day in our 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 - they depend on the compiler to flag all illegal uses of private property.

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.

To run the example you see, your browser must support:

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].
  • Currently only a few browsers support symbols.
  • They do not guarantee true privacy, but can be used to separate public and internal properties of objects. It is similar to how most object-oriented languages allow access to private properties through the reflection API.

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 is reachable from the CG roots, then every Person instance ever created is reachable and therefore cannot be garbage collected.

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.

Congratulations on completing this new tutorial, remember that on your website schoolJavaScript.com You will have access to the best JavaScript programming language courses.