Javascript. Prototype pattern

Паттерн Прототип (Prototype pattern) используется в тех случаях, когда создание экземпляра класса требует больших затрат ресурсов или занимает много времени. То есть определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор. Данный паттерн относится к порождающим шаблонам. Ниже приведены примеры реализации данного паттерна.

/* Cars */

var Nissan = function () {
    this.bodyType = 'sedan';
    this.numberOfWheels = 4;
    this.power = 100;
};

var Audi = function () {
    this.bodyType = 'sedan';
    this.numberOfWheels = 4;
    this.power = 150;
};

var Bmw = function () {
    this.bodyType = 'sedan';
    this.numberOfWheels = 4;
    this.power = 120;
};

/* CarFactory */

var CarFactory = function (nissan, audi, bmw) {
    if (nissan.constructor !== Nissan ||
        audi.constructor !== Audi ||
        bmw.constructor !== Bmw) {
        throw new Error("In argument given not instance");
    }
   
    var nissan = nissan,
        audi = audi,
        bmw = bmw;
   
    var isArray = function (object) {
        return Object.prototype.toString.call(object) === '[object Array]';
    };
   
    var    isObject = function (object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    };
   
    var clone = function (proto) {
        var newObject;  
      
        // for copying arrays
        if (isArray(proto)) {
            newObject = [];
            for (var i = 0, item, size = proto.length; i < size; i++) {
                if (isArray(proto[i]) || isObject(proto[i])) {
                    item = this.clone(proto[i]);
                }
                else {
                    item = proto[i];
                }
                newObject = newObject.concat(item);
            }          
        }
        // for copying objects
        else if (isObject(proto)) {
            newObject = {};          
            for (var property in proto) {
                if (proto.hasOwnProperty(property)) {
                    if (isArray(proto[i]) || isObject(proto[i])) {
                        newObject[property] = this.copy(proto[property]);
                    }
                    else {
                        newObject[property] = proto[property];
                    }
                }
            }   
        }
        // for copying other elements
        else {      
            newObject = proto;
        }
      
        return newObject;
    };
   
    this.getNissan = function () {
        return clone(nissan);
    };
   
    this.getAudi = function () {
        return clone(audi);
    };
   
    this.getBmw = function () {
        return clone(bmw);
    };
};

/* testing... */

var Application = function () {
    this.run = function () {
        var nissanPrototype = new Nissan(),
            audiPrototype = new Audi(),
            bmwPrototype = new Bmw();
       
        var factory = new CarFactory(nissanPrototype, audiPrototype, bmwPrototype);
       
        var nissan = factory.getNissan(),
            audi = factory.getAudi(),
            bmw = factory.getBmw();
    };
};


Данный пример реализован с помощью "фабричного" подхода. Вот иные реализации.

var ObjectPrototype = function (proto) {
   
    var isArray = function (object) {
        return Object.prototype.toString.call(object) === '[object Array]';
    };
   
    var    isObject = function (object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    };
   
    this.copy = function (proto) {     
        var newObject; 
     
        // for copying arrays
        if (isArray(proto)) {
            newObject = [];
            for (var i = 0, item, size = proto.length; i < size; i++) {
                if (isArray(proto[i]) || isObject(proto[i])) {
                    item = this.copy(proto[i]);
                }
                else {
                    item = proto[i];
                }
                newObject = newObject.concat(item);
            }         
        }
        // for copying objects
        else if (isObject(proto)) {
            newObject = {};         
            for (var property in proto) {
                if (proto.hasOwnProperty(property)) {
                    if (isArray(proto[i]) || isObject(proto[i])) {
                        newObject[property] = this.copy(proto[property]);
                    }
                    else {
                        newObject[property] = proto[property];
                    }
                }
            }         
        }
        // for copying other elements
        else {     
            newObject = proto;
        }
     
        return newObject;     
    };
};

var Man = function (name) {
    // some man properties...
};

var Woman = function (name) {
    // some woman properties...
};

var Application = function () {
    this.run = function () {
        var Adam = new Man('Adam');
        var AdamPrototype = new ObjectPrototype(Adam);
        var AdamClone = AdamPrototype.clone();
       
        var Eva = new Man('Eva');
        var EvaPrototype = new ObjectPrototype(Eva);
        var EvaClone = EvaPrototype.clone();
       
        // working with Adan & Eva clones
    };
};


Можно ObjectPrototype конкретизировать с помощью целевых классов.

var ObjectPrototype = function (proto) {
   
    var isArray = function (object) {
        return Object.prototype.toString.call(object) === '[object Array]';
    };
   
    var    isObject = function (object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    };
   
    this.copy = function (proto) {     
        var newObject; 
     
        // for copying arrays
        if (isArray(proto)) {
            newObject = [];
            for (var i = 0, item, size = proto.length; i < size; i++) {
                if (isArray(proto[i]) || isObject(proto[i])) {
                    item = this.copy(proto[i]);
                }
                else {
                    item = proto[i];
                }
                newObject = newObject.concat(item);
            }         
        }
        // for copying objects
        else if (isObject(proto)) {
            newObject = {};         
            for (var property in proto) {
                if (proto.hasOwnProperty(property)) {
                    if (isArray(proto[i]) || isObject(proto[i])) {
                        newObject[property] = this.copy(proto[property]);
                    }
                    else {
                        newObject[property] = proto[property];
                    }
                }
            }         
        }
        // for copying other elements
        else {     
            newObject = proto;
        }
     
        return newObject;     
    };
};

/* Customers */

var Customer = function (name, orders) {
    this.name = name || '';
    this.orders = orders || 0;
    this.setOrders = function (orders) {
        this.orders = orders;
    };
    this.getOrders = function () {
        return this.orders;
    };
};

var CustomerPrototype = function (customer) {
    if (!(customer instanceof Customer)) {
        throw new Error("Given argument is not Customer type");
    }
    this.setProto = function (_customer) {
        if (!(_customer instanceof Customer)) {
            throw new Error("Given argument is not Customer type");
        }
        customer = _customer;
    };
    this.getProto = function () {
        return this.copy(customer);
    };
};
CustomerPrototype.prototype = new ObjectPrototype();

CustomerPrototype.prototype.constructor = CustomerPrototype;
 
/* Sellers */

var Seller = function (name, sales) {
    this.name = name || '';
    this.sales = sales || 0;
    this.setSales = function (sales) {
        this.sales = sales;
    };
    this.getSales = function () {
        return this.sales;
    };
};

var SellerPrototype = function (seller) {
    if (!(seller instanceof Seller)) {
        throw new Error("Given argument is not Seller type");
    }
    this.setProto = function (_seller) {
        if (!(_seller instanceof Seller)) {
            throw new Error("Given argument is not Seller type");
        }
        seller = _seller;
    };
    this.getProto = function () {
        return this.copy(seller);
    };
};
SellerPrototype.prototype = new ObjectPrototype();

SellerPrototype.prototype.constructor = SellerPrototype;

/* testing... */

var Application = function () {
    this.run = function () {
        var Nik = new Customer('Nik', 125),
            NikCustomerPrototype = new CustomerPrototype(Nik),
            NikColleague = NikCustomerPrototype.getProto();
       
        console.log("Nik and his colleague have " + (Nik.getOrders() + NikColleague.getOrders()) + " orders");
       
        var Merry = new Seller('Merry', 16),
            MerrySellerPrototype = new SellerPrototype(Merry),
            FirstColleague = MerrySellerPrototype.getProto();
        MerrySellerPrototype.setProto(new Seller('Merry', 20));
        var SecondColleague = MerrySellerPrototype.getProto();
       
        console.log("Merry and her colleagues have " + (FirstColleague.getSales() + SecondColleague.getSales()) + " sales");
    };
};

var app = new Application();
app.run();


На выходе

Nik and his colleague have 250 orders
Merry and her colleagues have 36 sales