11
MyGlobalObject;

function TheFunctionICanUseRightAwaySingleForAllInstansesAndWithoutInstanse() {
    function() {
        alert('NO CONSTRUCTOR WAS CALLED');
    }
};

The Long-named function must be callable from MyGlobalObject, which in turn must be available as a global (to window) variable in all times after script was loaded. It should support extensibility in accordance with latest standards.

I'm at architectural dilemma of how to built JS base for an application (almost 100% JS).

We need an object i.e. window.MyObject (like a module, like jQuery) so

It can be created with

VAR1

 var MyGlobalObjConstructor = function(){
     this.GlobalFunctionInObject = function(){
        alert('called with MyGlobalObj.GlobalFunctionInObject()');
        }        
};
window.MyGlobalObj = new MyGlobalObjConstructor();    

Is MyGlobalObj extensible? Can I create child objects, which will inherit current state of MyGlobalObj (extended functions/properties MyGlobalObj.NewFunc e.g.)? What is the main difference between using prototype (VAR3)?

By GlobaldFunction I mean single instance for all initialized/instantiated (possibly instantializable) instances..

Or with

VAR2

var MyGlobalObj = {
    GlobalFunctionInObject: function...
    GlobalFunctionInObject2: function...
};
MyGlobalObj.GlobalFunctionInObject();
// here I lose all hierarchy elements, no prototype, 
// can I use GlobalFunctionInObject2 in GlobalFunctionInObject?

Or with

VAR3

var MyGlobalConstuctor = function(){} // already 'well-formed' object
MyGlobalConstuctor.prototype.GlobalFunctionInObject = function...
};
var MyGlobalObj = new MyGlobalConstuctor();

// so I'm sceptical to NEW, because I have ALREADY wrote my functions 
// which I expect to be in memory, single instance of each of them, 
// so creating MyObject2,3,4 with NEW MyGC() makes no sense to me.
// DO I REALLY HAVE TO USE "MyGlobalConstuctor.prototype." FOR EACH FUNCTION?!!!!

What's the difference defining MyGlobalObj as a function and as an object (result of func or VAR2)?

OR VAR4?

I see in Chrome Debugger both prototype and __proto__ special fields. I've read that that's OK, but why are they not saved in a single prototype?

So, what is the correct/optimal way to implement window.MyObject, so one could MyObject.MyFunction(); What are the differences (pro/contra) of variants 1 2 and 3?

6
  • Your "VAR1" sets window.MyGlobalObj to undefined and creates GlobalFunctionInObject() as a property of window - is that the intention? Commented Apr 17, 2013 at 8:32
  • What exactly do you want, a function or an object? Commented Apr 17, 2013 at 8:34
  • I'm having troubles to understand what you really want. Right now, this is an XY problem: You posted a set of solutions and ask which one to choose (basically), but you haven't properly explained the actual problem you are trying to solve. Please provide an example use case. Commented Apr 17, 2013 at 8:34
  • For your edited "VAR1", now you are setting window.MyGlobalObj equal to window, and still creating GlobalFunctionInObject() as a property (method) of window. (Which obviously still isn't what you said that code should do.) I'm assuming, of course, that you'd be running that code at the "top level", in which case this will be equal to window... Commented Apr 17, 2013 at 8:37
  • @FelixKling In the end I want to call MyGlobalObject.MyFunction(), but I want define MyGlobalObject properly to support inheritance and exclude possible overheads in the future, when I extend the "class/object". The solutions are not ideal, I would like to know if my reasoning is right and want have some troubling/unclear questions answered. Commented Apr 17, 2013 at 8:40

2 Answers 2

32

Variation 1 - Mixin

function SomeType() {
    var priv = "I'm private";
    this.publ = "I'm public";
    this.action = function() {
        return priv + this.publ;
    };
}

var obj = new SomeType();

With this method you are creating a new object every time you call new SomeType(), creating all its methods and adding all this method to the new object. Every time you create an object.

Pros

  • It looks like classical inheritance so it's easy to understand to Java-C#-C++-etc people.
  • It can have private variables per instance since you have one function closure per each object you create
  • It allows multiple inheritance, also known as Twitter-mixins or functional mixins
  • obj instanceof SomeType will return true

Cons

  • It consumes more memory as more objects you create because with each object you are creating a new closure and creating each of it's methods again.
  • Private properties are private, not protected, subtypes can't access them
  • No easy way to know if a object has some Type as superclass.

Inheritance

function SubType() {
    SomeType.call(this);
    this.newMethod = function() {
        // can't access priv
        return this.publ;
    };
}

var child = new SubType();

child instanceof SomeType will return false there is no other way to know if child has SomeType methods than look if it has them one by one.

Variation 2 - Object literal with prototyping

var obj = {
    publ: "I'm public",
    _convention: "I'm public too, but please don't touch me!",
    someMethod: function() {
        return this.publ + this._convention;
    }
};

In this case you are creating a single object. If you are going to need only one instance of this type it can be the best solution.

Pros

  • It's quick and easy to understand.
  • Performant

Cons

  • No privacy, every property is public.

Inheritance

You can inherit a object prototyping it.

var child = Object.create(obj);
child.otherMethod = function() {
    return this._convention + this.publ;
};

If you are on a old browser you will need to garantee Object.create works:

if (!Object.create) {
    Object.create = function(obj) {
        function tmp() { }
        tmp.prototype = obj;
        return new tmp;
    };
}

To know if a object is a prototype of another you can use

obj.isPrototypeOf(child); // true

Variation 3 - Constructor pattern

UPDATE: This is the pattern ES6 classes are sugar syntax of. If you use ES6 classes you are following this pattern under the hood.

class SomeType {
    constructor() {
        // REALLY important to declare every non-function property here
        this.publ = "I'm public";
        this._convention = "I'm public too, but please don't touch me!";
    }
    someMethod() {
        return this.publ + this._convention;
    }
}

class SubType extends SomeType {
    constructor() {
        super(/* parent constructor parameters here */);
        this.otherValue = 'Hi';
    }
    otherMethod() {
        return this._convention + this.publ + this.otherValue;
    }
}

function SomeType() {
    // REALLY important to declare every non-function property here
    this.publ = "I'm public";
    this._convention = "I'm public too, but please don't touch me!";
}

SomeType.prototype.someMethod = function() {
    return this.publ + this._convention;
};

var obj = new SomeType();

You can re-assign the prototype insteadd of adding each method if you are not inheriting and remember to re-assign the constructor property:

SomeType.prototype = {
    constructor: SomeType,
    someMethod = function() {
        return this.publ + this._convention;
    }
};

Or use _.extend or $.extend if you have underscore or jquery in your page

_.extend(SomeType.prototype, {
    someMethod = function() {
        return this.publ + this._convention;
    }
};

The new keyword under the hood simply does this:

function doNew(Constructor) {
    var instance = Object.create(Constructor.prototype);
    instance.constructor();
    return instance;
}

var obj = doNew(SomeType);

What you have is a function than has no methods; it just has a prototype property with a list of functions, the new operator means to create a new object and use this function's prototype (Object.create) and constructor property as initializer.

Pros

  • Performant
  • Prototype chain will allow you to know if a object inherits from some type

Cons

  • Two-step inheritance

Inheritance

function SubType() {
    // Step 1, exactly as Variation 1
    // This inherits the non-function properties
    SomeType.call(this);
    this.otherValue = 'Hi';
}

// Step 2, this inherits the methods
SubType.prototype = Object.create(SomeType.prototype);
SubType.prototype.otherMethod = function() {
    return this._convention + this.publ + this.otherValue;
};

var child = new SubType();

You may think it looks like a super-set of Variation 2... and you'll be right. It's like variation 2 but with a initializer function (the constructor);

child instanceof SubType and child instanceof SomeType will return both true

Curiosity: Under the hood instanceof operator does is

function isInstanceOf(obj, Type) {
    return Type.prototype.isPrototypeOf(obj);
}

Variation 4 - Overwrite __proto__

When you do Object.create(obj) under the hood it does

function fakeCreate(obj) {
    var child = {};
    child.__proto__ = obj;
    return child;
}

var child = fakeCreate(obj);

The __proto__ property modifies directly the object's hidden [Prototype] property. As this can break JavaScript behaviour, it's not standard. And the standard way is preferred (Object.create).

Pros

  • Quick and performant

Cons

  • Non-standard
  • Dangerous; you can't have a hashmap since the __proto__ key can change the object's prototype

Inheritance

var child = { __proto__: obj };
obj.isPrototypeOf(child); // true

Comment questions

1. var1: what happens in SomeType.call(this)? Is 'call' special function?

Oh, yes, functions are objects so they have methods, I will mention three: .call(), .apply() and .bind()

When you use .call() on a function, you can pass one extra argument, the context, the value of this inside the function, for example:

var obj = {
    test: function(arg1, arg2) {
        console.log(this);
        console.log(arg1);
        console.log(arg2);
    }
};

// These two ways to invoke the function are equivalent

obj.test('hi', 'lol');

// If we call fn('hi', 'lol') it will receive "window" as "this" so we have to use call.
var fn = obj.test;
fn.call(obj, 'hi', 'lol');

So when we do SomeType.call(this) we are passing the object this to function SomeCall, as you remember this function will add methods to object this.

2. var3: With your "REALLY define properties" do you mean if I use them in functions? Is it a convention? Because getting this.newProperty without it being defined at the same level with other member functions is not a problem.

I mean any property your object will have that is not a function must be defined on the constructor, not on the prototype, otherwise you will face one of the more confusing JS problems. You can see it here, but it's outside of the focus of this question.

3. Var3: what happens if I don't re-assign constructor?

Actually you might not see the difference and this is what makes it a dangerous bug. Every function's prototype object has a constructor property so you can access the constructor from an instance.

function A() { }

// When you create a function automatically, JS does this:
// A.prototype = { constructor: A };

A.prototype.someMethod = function() {
    console.log(this.constructor === A); // true
    this.constructor.staticMethod();
    return new this.constructor();  
};

A.staticMethod = function() { };

It's not a best practice because not everybody knows about it, but sometimes it helps. But if you reassign the prototype...

A.prototype = {
    someMethod = function() {
        console.log(this.constructor === A); // false
        console.log(this.constructor === Object); // true
        this.constructor.staticMethod();
        return new this.constructor();  
    }
};

A.prototype is a new object, a instance of Object than prototypes Object.prototype and Object.prototype.constructor is Object. Confusing, right? :P

So if you overwrite the prototype and don't reset the "constructor" property, it will refer to Object instead of A, and if you try to use the "constructor" property to access some static method you may get crazy.

Sign up to request clarification or add additional context in comments.

4 Comments

Hey, thanks, that's heavy and deep! 1. var1: what happens in SomeType.call(this) is 'call' special function? 2. var3: With your "REALLY define properties" do you mean if I use them in functions? Is it a convention? Because getting this.newProperty without it being defined at the same level with other member functions is not a problem. 3. Var3: what happend if I don't re-assign constructor?
Wow, that's genius! Sorry for many questions as well, we'll hope this will help many other people! To Summarize: 1. if I don't want to type word 'prototype' for each member definition, then the only way to "expand" prototype is by $.extend(obj, {func1: function(){}}), so we must have jQuery loaded and extend is more heavy then typing "prototype" for each member. 2. In your answer to comment questions you use A.staticMethod(). This means it exists without an instance (created with "new"), but for every new instance there will be actually a COPY of staticMethod. (so static != static in C# i.e.).
Not really, a "staticMethod" is a method on the constructor function. The instances will not have this method, but they can access it from the constructor accessing it by it's name (recommended) A.staticMethod() or the constructor property this.constructor.staticMethod()
JQuery just for this is a little bit heavy for this but I recomend you Underscore, performance it's important but also is important to write simple and readable code and it helps a lot. BTW I'll traslate this and post it on my blog so I'm glad to have questions :P
5

I usually settle with returning an object with functions as properties:

var newCat = function (name) {
return {name: name, purr: function () {alert(name + ' purrs')}};
};

var myCat = newCat('Felix');
myCat.name; // 'Felix'
myCat.purr(); // alert fires

You can have inheritance by calling the newCat function and extend the object you get:

var newLion = function (name) {
    var lion = newCat(name);
    lion.roar = function () {
        alert(name + ' roar loudly');
    }
    return lion;
}

If you want a global cats object:

var cats = (function () {

var newCat = function (name) {
    return {
        name: name,
        purr: function () {
            alert(name + ' is purring')
        }
    };
};

return {
    newCat: newCat
};
}());

Now you can call:

var mySecondCat = cats.newCat('Alice');

2 Comments

so you propose VAR2? I don't like it the most), what if there's the same function for all cats.. like getConstantValue().. it will be created N-times for each cat, which is irrational memory use
In the cats object, you can make a function that returns that constant value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.