/clonejs-nano

Fast JavaScript object creation pattern in three lines of JavaScript

Primary LanguageJavaScriptMIT LicenseMIT

clone.js nano

The true prototype-based⠙ three-lines "framework". The replacement for the new operator. This is it:

function clone(/** Object */proto, /** object.literalOnly! */ownProperties){
    ownProperties.__proto__ = proto;
    return ownProperties;
}

The __proto__ is a part of upcoming ECMA Script 6⠙ standart. Currently, all major browsers have __proto__ support, except Internet Explorer.
This clone function can be also implemented through Object.create or function-constructors (JavaScript 1.0 / IE3.0).

What is the Clone?

clone(obj, {}) function produces new1 objects — Clones.
Clone object — this is the lazy shallow copy, i.e., it is actually not a copy, it's just a reference to the object, with one difference: if you will add/replace any of its properties, it would not affect the cloned object (prototype).
All JavaScript objects are clones of Object.prototype (except itself and objects, created by Object.create(null)).

1 To make it true, you need to follow one rule:
The second argument of the clone(obj, {}) function should not be a variable. Only object literal ({}) allowed.

Why not Object.create?

  1. Because its second argument isn't usable:
var talkingDuck = Object.create(duck, {
    firstName: {value:"", enumerable:true, writable:true},
    lastName: {value:"Duck", enumerable:true, writable:true},

    quack: {value: function(){
        duck.quack.call(this);
        console.log("My name is "+ this.name +"!");
    }}
});
  1. It's slow.

Try the true prototype-based OOP!

With this "framework" you can easilly create and manipulate objects without constructors, instead of classic js way, where you should define a constructor for every object (that you want to use as prototype), even if you didn't need it. It's possible to build and maintain extremely large numbers of "classes" with comparatively little code.

It's trivial to create new "classes" - just clone the object and change a couple of properties and voila... new "class".

It's really class-free: clone() produces objects (prototypes), not function-constructors, unlike all other class-producing tools (Ext.define, dojo.declare etc).

Read more:

It really fast!

It faster than any other framework, even VanillaJS! Yes, it creates class-objects faster than JS core creates class-functions!
See http://jsperf.com/fw-class-creation/3 and http://jsperf.com/clonejs-nano-vs-vanillajs/5 CloneJS Nano vs VanillaJS

How to use

Forget about classes (function-constructors).
Instead of creating class (function), create prototype (object):

var duck = {
    name:  "Duck",
    color: "",
    canFly: true,
    quack: function(){
        console.log( this.name +": Quack-quack!");
    }
};

The classic way:

var Duck = function(name, color, canFly){
    this.name  = name  || "Duck";
    this.color = color || "";
    this.canFly= canFly === undefined ? true : canFly;  
}
Duck.prototype.quack = function(){
    console.log(this.name +": Quack-quack!");
}

Inheritance is simple (talkingDuck prototype extends duck prototype):

var talkingDuck = lazyClone( duck, {
    firstName: "",
    lastName: "Duck",
    
    quack: function(){
        duck.quack.call(this);
        console.log("My name is "+ this.name +"!");
    },
    // backward compatibility with duck interface:
    get name(){
        return (this.firstName +" "+ this.lastName).trim();
    },
    set name(newName){
        var names = newName.split(" ");
        this.firstName = names[0];
        if(names.length > 1){
            this.lastName = names[1];
        }
    }    
});

The classic way:

var TalkingDuck = function(firstName, lastName, color, canFly){
    this.firstName = firstName;
    this.lastName = lastName || "Duck";
    this.color = color || "";
    this.canFly= canFly === undefined ? true : canFly;
}
var TalkingDuckPrototype = function(){};
TalkingDuckPrototype.prototype = Duck.prototype;
TalkingDuck.prototype = new TalkingDuckPrototype;
TalkingDuck.prototype.constructor = TalkingDuck;
TalkingDuck.prototype.quack = function(){
    Duck.prototype.quack.call(this);
    console.log("My name is "+ this.name +"!");
}
// backward compatibility with Duck interface:
Object.defineProperty(TalkingDuck.prototype, 'name', {
    get: function(){
        return (this.firstName +" "+ this.lastName).trim();
    },
    set: function(newName){
        var names = newName.split(" ");
        this.firstName = names[0];
        if(names.length > 1){
            this.lastName = names[1];
        }
    }
});

Forget about the new operator, use clone to create instances:

var donald = clone(talkingDuck, {firstName: "Donald", color: "White", canFly: false});
donald.quack();// Donald Duck: Quack-quack! 
               // My name is Donald!

The classic way:

var daffy = new TalkingDuck("Daffy", undefined, "Black", false);
daffy.quack();// Daffy Duck: Quack-quack! 
               // My name is Daffy!

Forget about the instanceof operator, use JS native .isPrototypeOf() method instead:

duck.isPrototypeOf(donald);// true

The classic way:

daffy instanceof Duck;// true

Object-oriented notation

Create the root prototype for all your objects:

var object = {
    clone: function(/** object.literalOnly! */ownProperties){
        ownProperties.__proto__ = this;
        return ownProperties;
    }
}

After that, you can clone it:

var duck = object.clone({
    name: "Duck",
    quack: function(){
        console.log(this.name +": Quack-quack!");
    }
});

var donald = duck.clone({name: "Donald Duck"});

or just copy its clone method to your prototype:

var duck = {
    name: "Duck",
    quack: function(){
        console.log(this.name +": Quack-quack!");
    },
    clone: object.clone
};

var donald = duck.clone({name: "Donald Duck"});

How to initialize object by calculated value?

1st, classic way:

constructor
var obj = {
    base: 1000,
    constructor: function(initBy){
        if(initBy === undefined) initBy = 1;
        this.num = this.base * initBy;
        console.log("property calculated and stored");
    }
}
obj.constructor.prototype = obj;

var instance = new obj.constructor(777);
// property calculated and stored

var num = instance.num;

console.log(instance.num);
// 1777

The second, more interesting way:

Lazy initialization
var obj = {
    base: 1000,
    initBy: 1,
    
    get num(){
        console.log("property calculated");
        return this.num = this.base * this.initBy;
    },
    set num(newValue){
        console.log("and stored");
        Object.defineProperty(this, 'num', {value: newValue, writable:true,enumerable:true});
    }
}

var instance = clone(obj, {initBy: 777});

var num = instance.num;
// property calculated
// and stored

console.log(instance.num);// num isn't calculated again
// 1777

The benefits of this technic is:

  • separation of concerns: constructor separation into small accessor methods (to prevent "long method" antipattern)
  • perfomance boost, because unused properties will be not initialized

If you like the idea, plese look at the extended version of this framework www.github.com/quadroid/clonejs