0

I'm working on a large JavaScript UI module, think about something in the dimension of iScroll. For keeping the thing sane, I split the library up into several logical sub-modules (like: touch interaction, DOM interaction, etc.) that communicate with each other using a simple pubsub system.

Each of these sub-modules has it's own namespace I use for storing variables. To enable several instances of the library running at the same time, the constructor includes some logic to copy every namespace that has been assigned to a 'toCopy' object directly into every new instance.

The basic structure looks something like this:

// constructor

function Constructor() {
  for (var key in this.toCopy) {
    // create a deep copy and store it inside the instance
    this[key] = utils.copyObj(this.toCopy[key]);
  }
}

Constructor.prototype.toCopy = {}


// sub-module #1

Constructor.prototype.toCopy._module1Namespace = {}

Constructor.prototype._privateFn = function() { /** do stuff within this._module1Namespace or send events */ }
Constructor.prototype._privateFn = function() { /** do stuff within this._module1Namespace or send events */ }


// sub-module #2

Constructor.prototype.toCopy._module2Namespace = {}

Constructor.prototype._privateFn = function() { /** do stuff with this._module2Namespace or send events */ }
Constructor.prototype._privateFn = function() { /** do stuff with this._module2Namespace or send events */ }

My problem: I really dislike having to expose the namespace of each sub-module to the rest of the library. Although I'm disciplined enough not to access the namespace of sub-module #1 within a function of sub-module #2, I want it to be technically impossible.

However, as several instances of the UI library have to run at the same time, I cannot put every sub-module inside a closure and define a private namespace inside this closure. This would result in every instance of the library accessing the same sub-module closure and therefore the same namespace.

Also, putting everything inside the constructor instead of into the prototype would create a new set of functions for every new instance, which somehow contradicts the idea of a good library.

My research hasn't brought up any useful patterns for this problem so far, I'm thankful for all suggestions.

zerodot
  • 805
  • 1
  • 7
  • 15
  • Should `Constructor.toCopy = {}` be `Constructor.prototype.toCopy = {}`? What does `utils.copyObj` do? Should the for..in loop use a *hasOwnProperty* test? Perhaps you need to investigate the [*module pattern*](http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html)? [*Google: javascript module pattern*](https://www.google.com/search?client=safari&rls=en&q=javascript+module+pattern&ie=UTF-8&oe=UTF-8) – RobG Dec 02 '14 at 02:14
  • #toCopy: Thank you for noticing, it's indeed Constructor.prototype.toCopy. However, this mistake just happened in the example code. #utils.copyObj: This creates a deep copy of an object: utils.copyObj = function(source) { if(source === null || typeof(source) !== 'object') return source; var newObj = source.constructor(); for (var key in source) { if(source.hasOwnProperty(key)) { newObj[key] = utils.copyObj(source[key]); } } return newObj; }; #Module pattern: Investigated, don't think it helps, need to put fns into the prototype. Can you give me a concrete example? – zerodot Dec 02 '14 at 09:39
  • if you want to take a better look at the copyObj code: http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object – zerodot Dec 02 '14 at 09:56
  • 1
    I don't know why that answer is so highly voted, e.g. expecting `obj.constructor()` in a general case to reliably return an Object is not sensible. Most constructors, when called as functions, will return *undefined*. I don't think I understand what you are trying to do. "*several instances of the UI library have to run at the same time*" doesn't make sense, several instances of an object does. "*putting everything inside the constructor ... contradicts the idea of a good library*", but isn't that what you are attempting with *copyObj*? – RobG Dec 02 '14 at 12:02
  • #instances: jep, several instances of the object. but that's semantics. #constructor: if we put all functions inside the constructor, they get duplicated in the memory on every new instance. by putting them into the prototype, every instance uses the same reference to the function. the only thing i'm duplicating are the variables the functions access / change (because every instance needs it's own copy). – zerodot Dec 02 '14 at 17:49
  • I guess I'm struggling to see how you are doing anything other than plain prototype inheritance: assign properties in the constructor, inherit methods from its prototype. I'll have a stab at an answer shortly. – RobG Dec 02 '14 at 23:20

1 Answers1

1

It seems that you want instances to have a standard set of properties that are defined in some object, not explicitly assigned in the constructor. The "standard" pattern would be:

function Foo(a, b) {
  this.a = a;
  this.b = b;
}

Foo.prototype.fn = function() {
  // do stuff
}

However, you seem to want to create an object that has standard properties and perhaps default values. Maybe that should be a property of the constructor:

Foo.standardProps = {
  a: 'a',
  b: 'b'
}

Now in the constructor you can do:

function Foo() {
  cloneObj(Foo.standardProps, this);
}

Where cloneObj does the copy to your specification.

Note that there are lots of questions and answers about how to copy or clone a javascript object. It's quite difficult do to in a general case as there are differing opinions on what a copy is vs a clone, the different types of object and also how to treat non–Object objects like Arrays, Dates and Functions and also host objects like DOM elements or even the Window object.

So best to define a limited case for the types of Object you wish to make "deep" copies of. You may even want to use getters and setters to deal with each case explicitly.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Thanks for your answer! Unfortunately it's not the thing I'm looking for but I'll outline the problem in a blogpost within the next few days (hopefully) and keep this thread up to date. – zerodot Dec 08 '14 at 10:06