Book Image

Javascript Unlocked

Book Image

Javascript Unlocked

Overview of this book

JavaScript stands bestride the world like a colossus. Having conquered web development, it now advances into new areas such as server scripting, desktop and mobile development, game scripting, and more. One of the most essential languages for any modern developer, the fully-engaged JavaScript programmer need to know the tricks, non-documented features, quirks, and best practices of this powerful, adaptive language. This all-practical guide is stuffed with code recipes and keys to help you unlock the full potential of JavaScript. Start by diving right into the core of JavaScript, with power user techniques for getting better maintainability and performance from the basic building blocks of your code. Get to grips with modular programming to bring real power to the browser, master client-side JavaScript scripting without jQuery or other frameworks, and discover the full potential of asynchronous coding. Do great things with HTML5 APIs, including building your first web component, tackle the essential requirements of writing large-scale applications, and optimize JavaScript’s performance behind the browser. Wrap up with in-depth advice and best practice for debugging and keeping your JavaScript maintainable for scaling, long-term projects. With every task demonstrated in both classic ES5 JavaScript and next generation ES6-7 versions of the language, Whether read cover-to-cover or dipped into for specific keys and recipes, JavaScript Unlocked is your essential guide for pushing JavaScript to its limits.
Table of Contents (15 chapters)
JavaScript Unlocked
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface
Index

The most effective way of declaring objects


How do we declare an object in JavaScript? If we need a namespace, we can simply use an object literal. But when we need an object type, we need to think twice about what approach to take, as it affects the maintainability of our object-oriented code.

Classical approach

We can create a constructor function and chain the members to its context:

"use strict"; 
/**
 * @class
 */
var Constructor = function(){
   /**
   * @type {String}
   * @public
   */
   this.bar = "bar";
   /**
   * @public
   * @returns {String}
   */
   this.foo = function() {
    return this.bar;
   };
 },
 /** @type Constructor */
 instance = new Constructor();

console.log( instance.foo() ); // bar
console.log( instance instanceof Constructor ); // true

We can also assign the members to the constructor prototype. The result will be the same as follows:

"use strict";
/**
* @class
*/
var Constructor = function(){},
   instance;
/**
* @type {String}
* @public
*/
Constructor.prototype.bar = "bar";
/**
* @public
* @returns {String}
*/
Constructor.prototype.foo = function() {
 return this.bar;
};
/** @type Constructor */
instance = new Constructor();

console.log( instance.foo() ); // bar
console.log( instance instanceof Constructor ); // true

In the first case, we have the object structure within the constructor function body mixed with the construction logic. In the second case by repeating Constructor.prototype, we violate the Do Not Repeat Yourself (DRY) principle.

Approach with the private state

So how can we do it otherwise? We can return an object literal by the constructor function:

"use strict";
/**
 * @class
 */
var Constructor = function(){
     /**
     * @type {String}
     * @private
     */
     var baz = "baz";
     return {
       /**
       * @type {String}
       * @public
       */
       bar: "bar",
       /**
       * @public
       * @returns {String}
       */
       foo: function() {
        return this.bar + " " + baz;
       }
     };
   },
   /** @type Constructor */
   instance = new Constructor();

console.log( instance.foo() ); // bar baz
console.log( instance.hasOwnProperty( "baz") ); // false
console.log( Constructor.prototype.hasOwnProperty( "baz") ); // false
console.log( instance instanceof Constructor ); // false

The advantage of this approach is that any variables declared in the scope of the constructor are in the same closure as the returned object, and therefore, available through the object. We can consider such variables as private members. The bad news is that we will lose the constructor prototype. When a constructor returns an object during instantiation, this object becomes the result of a whole new expression.

Inheritance with the prototype chain

What about inheritance? The classical approach would be to make the subtype prototype an instance of supertype:

"use strict";
 /**
 * @class
 */
var SuperType = function(){
       /**
       * @type {String}
       * @public
       */
       this.foo = "foo";
     },
     /**
      * @class
      */
     Constructor = function(){
       /**
       * @type {String}
       * @public
       */
       this.bar = "bar";
     },
     /** @type Constructor */
     instance;

 Constructor.prototype = new SuperType();
 Constructor.prototype.constructor = Constructor;

 instance = new Constructor();
 console.log( instance.bar ); // bar
 console.log( instance.foo ); // foo
 console.log( instance instanceof Constructor ); // true
 console.log( instance instanceof SuperType ); // true  

You may run into some code, where for instantiation Object.create is used instead of the new operator. Here you have to know the difference between the two. Object.create takes an object as an argument and creates a new one with the passed object as a prototype. In some ways, this reminds us of cloning. Examine this, you declare an object literal (proto) and create a new object (instance) with Object.create based on the first one. Whatever changes you do now on the newly created object, they won't be reflected on the original (proto). But if you change a property of the original, you will find the property changed in its derivative (instance):

"use strict";
var proto = {
 bar: "bar",
 foo: "foo"
}, 
instance = Object.create( proto );
proto.bar = "qux",
instance.foo = "baz";
console.log( instance ); // { foo="baz",  bar="qux"}
console.log( proto ); // { bar="qux",  foo="foo"}

Inheriting from prototype with Object.create

In contrast to the new operator, Object.create does not invoke the constructor. So when we use it to populate a subtype prototype, we are losing all the logic located in a supertype constructor. This way, the supertype constructor is never called:

// ...
SuperType.prototype.baz = "baz";
Constructor.prototype = Object.create( SuperType.prototype );
Constructor.prototype.constructor = Constructor;

instance = new Constructor();

console.log( instance.bar ); // bar
console.log( instance.baz ); // baz
console.log( instance.hasOwnProperty( "foo" ) ); // false
console.log( instance instanceof Constructor ); // true
console.log( instance instanceof SuperType ); // true

Inheriting from prototype with Object.assign

When looking for an optimal structure, I would like to declare members via an object literal, but still have the link to the prototype. Many third-party projects leverage a custom function (extend) that merge the structure object literal into the constructor prototype. Actually, ES6 provides an Object.assign native method. We can use it as follows:

"use strict";
   /**
    * @class
    */
var SuperType = function(){
     /**
     * @type {String}
     * @public
     */
     this.foo = "foo";
   },
   /**
    * @class
    */
   Constructor = function(){
     /**
     * @type {String}
     * @public
     */
     this.bar = "bar";
   },
   /** @type Constructor */
   instance;

Object.assign( Constructor.prototype = new SuperType(), {
 baz: "baz"
});
instance = new Constructor();
console.log( instance.bar ); // bar
console.log( instance.foo ); // foo
console.log( instance.baz ); // baz
console.log( instance instanceof Constructor ); // true
console.log( instance instanceof SuperType ); // true

This looks almost as required, except there is one inconvenience. Object.assign simply assigns the values of source objects to the target ones regardless of their type. So if you have a source property with an object (for instance, an Object or Array instance), the target object receives a reference instead of a value. So you have to reset manually any object properties during initialization.

Approach with ExtendClass

ExtendClass, proposed by Simon Boudrias, is a solution that seems flawless (https://github.com/SBoudrias/class-extend). His little library exposes the Base constructor with the extend static method. We use this method to extend this pseudo-class and any of its derivatives:

"use strict";
   /**
    * @class
    */
var SuperType = Base.extend({
     /**
      * @pulic
      * @returns {String}
      */
     foo: function(){ return "foo public"; },
     /**
      * @constructs SuperType
      */
     constructor: function () {}
   }),
   /**
    * @class
    */
   Constructor = SuperType.extend({
     /**
      * @pulic
      * @returns {String}
      */      
     bar: function(){ return "bar public"; }
   }, {
     /**
      * @static
      * @returns {String}
      */      
     bar: function(){ return "bar static"; }
   }),
   /** @type Constructor */
   instance = new Constructor();
   
console.log( instance.foo() ); // foo public
console.log( instance.bar() ); // bar public
console.log( Constructor.bar() ); // bar static
console.log( instance instanceof Constructor ); // true
console.log( instance instanceof SuperType ); // true

Classes in ES6

TC39 (the EcmaScript working group) is pretty aware of the problem, so the new language specification provides extra syntax to structure object types:

"use strict";
class AbstractClass {
 constructor() {
   this.foo = "foo";
 }
}
class ConcreteClass extends AbstractClass {
 constructor() {
   super();
   this.bar = "bar";
 }
 baz() {
   return "baz";
 }
}

let instance = new ConcreteClass();
console.log( instance.bar ); // bar
console.log( instance.foo ); // foo
console.log( instance.baz() ); // baz
console.log( instance instanceof ConcreteClass ); // true
console.log( instance instanceof AbstractClass ); // true

The syntax looks class-based, but in fact this a syntactic sugar over existing prototypes. You can check with the type of ConcreteClass, and it will give you function because ConcreteClass is a canonical constructor. So we don't need any trick to extend supertypes, no trick to refer the supertype constructor from subtype, and we have a clean readable structure. However, we cannot assign properties the same C-like way as we do now with methods. This is still in discussion for ES7 (https://esdiscuss.org/topic/es7-property-initializers). Besides this, we can declare a class's static methods straight in its body:

class Bar {
 static foo() {
   return "static method";
 }
 baz() {
   return "prototype method";
 }
}
let instance = new Bar();
console.log( instance.baz() ); // prototype method
console.log( Bar.foo()) ); // static method

Actually, there are many in the JavaScript community who consider the new syntax as a deviation from the prototypical OOP approach. On the other hand, the ES6 classes are backwards compatible with most of the existing code. Subclasses are now supported by the language and no extra libraries are required for inheritance. And what I personally like the most is that this syntax allows us to make the code cleaner and more maintainable.