5

I have three ways of making a function and returning it. (Maybe there is more?) but I don't know the differnce between them and when to use which. Could someone please explain.

var test1 = function() {
    var funk1 = function() {
        console.log(1);
    }
    var funk2 = function(msg) {
        console.log(msg);
    }
    return {
        funk1: funk1,
        funk2: funk2
    }
};

var test2 = function() {
    this.funk1 = function() {
        console.log(1);
    }
    this.funk2 = function(msg) {
        console.log(msg);
    }
};

var someThing = test1();
someThing.funk1();
someThing.funk2(2);

var someThing = new test1();
someThing.funk1();
someThing.funk2(2);

var thingElse = new test2();
thingElse.funk1();
thingElse.funk2(2);
Szabolcs Dézsi
  • 8,743
  • 21
  • 29

1 Answers1

4

This is module/library/plugin pattern

var test1 = function() {
    var funk1 = function() {
        console.log(1);
    }
    return {
        funk1: funk1
    }
};

This is OOP(Object-Oriented-Programming) pattern

var test2 = function() {
    this.funk1 = function() {
        console.log(1);
    }
};

Difference

var t = test1() //function call, t is result of test1 function execution
var t = new test1() //instance initialization, t is instance of test1 class

Consider this simple model

We have a manufacturer that makes cars, manufacturer has some methods, and a car has it's own methods, we won't to tweak into manufacturer public properties, but still we cannot access the technology that manufacturer uses to create a car, so car is black box, that exposes some public props, and cannot be tweaked.

//service for creating/controlling manufactorer
var ManufacturerService = function(companyName) {
    var self = this;
    self.companyName = companyName;

    //encapsulation
    var Car = function(name, number){
        var createdAt = new Date();
        var engineStarts = 0;

        //instance property, doesn't have setter
        Object.defineProperty(this, 'info', {
            get: function(){
                return {
                    name: name,
                    createdAt: createdAt,
                    engineStarts: engineStarts
                }
            }
        });

        //instance method
        this.startEngine = function(){
            //private property, available only on instance, and cannot be extracted
            engineStarts++;

            //reference to ManufacturerService.companyName
            console.log(self.companyName + ' ' + name + ':' + number + ' engine Started');
        }
    }
    var createCar = function(name){
        //check cache/duplication/validation
        //or use custom behavior
        var carNumber = ManufacturerService.genShortUid();
        return new Car(name, carNumber);
    }
    var getName = function(){
        return self.companyName;
    }
    return {
        getName: getName,
        createCar: createCar
    }
};
//static method, this can be overriden by 3rdParty or user 
ManufacturerService.genShortUid = function genShortUid() {
    return ('0000' + (Math.random()*Math.pow(36,4) << 0).toString(36)).slice(-4);
}

Usage, the ManufacturerService is written as plugin/library, but notice that Car model is fully encapsulated, which means model is totally hidden from outside world

//create a service instance
var VolvoCompany = new ManufacturerService('Volvo');

//access encapsulated Car model by createCar method
//which is available only on instance of service
var Golf = VolvoCompany.createCar('Golf');

//instance method of car Model
Golf.startEngine(); //logs Volvo Golf:vnv6 engine Started

Golf.info
//returns 
Object {name: "Golf", createdAt: Sat Feb 13 2016 17:39:57 GMT+0600 (ALMT), engineStarts: 1}

//try to hack car instance
Golf.info.name = 'test';
Golf.name = 'test';

Golf.info
//returns same object, as Car model doesn't have exported setters
Object {name: "Golf", createdAt: Sat Feb 13 2016 17:39:57 GMT+0600 (ALMT), engineStarts: 1}    

//customize guid generator of ManufacturerService
ManufacturerService.genShortUid = function(){
    return '1111';
}

//reuse same service for another Car model
var Mazda = VolvoCompany.createCar('Mazda');

//instance method of car Model
Mazda.startEngine(); //logs Volvo Mazda:1111 engine Started

Summary

ManufacturerService exports limited amount of methods, and totally open, but Car instance is totally encapsulated and not even visible

edits/suggestions/comments are welcome

Medet Tleukabiluly
  • 11,662
  • 3
  • 34
  • 69
  • is there a difference between test1() and new test1()? they should do the exact same thing right? – pailhead Feb 13 '16 at 11:58
  • `new test1()` is instance initialization, `test1()` is just a function call – Medet Tleukabiluly Feb 13 '16 at 12:01
  • right but both return an object literal, with a closure. I guess what im asking is, the title says "three different..." but it seems like its only two different ways. The first two seem the same. A different way would be using test1 as a singleton, by doing someThing = test1 instead of calling the function. Since there is nothing in the closure thats not exposed, and nothing to change the behavior of these functions, this particular one may as well just be used like that. – pailhead Feb 13 '16 at 12:03
  • This is the thing i'm struggling to simplify/explain with words, consider completing answer or suggesting idea – Medet Tleukabiluly Feb 13 '16 at 12:05
  • Im just wondering if the op meant to write one of the two examples as a singleton, but im not 100% sure that the first two do exactly the same thing. – pailhead Feb 13 '16 at 12:09