2

I have an instance function in javascript and for naming conventions I have other instance function added as property of the first instance function object. It's better illustrated in the following working JavaScript.

var MyModule = (function() { // instance function 
    return function() {
      console.log("ran MyModule");
    }
}());

MyModule.RelatedModule = (function() { //second instance function is a property of first instance function object
    return function() {
        console.log("ran RelatedModule");
    }
}())

var inst1 = new MyModule(), // logs "ran MyModule"
    inst2 = new MyModule.RelatedModule(); // logs "ran RelatedModule"

This works as intended with no errors. What I'd like to do though is to create the function definition for MyModule after I've created the MyModule object, can anyone help me achieve this? I've illustrated my attempts below.

var MyModule = {}; // create object first, try to set a constructor on it later

MyModule.RelatedModule = (function() { // add properties to the MyModule object
    return function() {
        console.log("ran RelatedModule");
    }
}())

// the following does not work, I'd like to set the `MyModule` constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.constructor = (function() {
    return function() {
      console.log("ran MyModule");
    }
}());

So, how do I retain the properties of an object and change it's constructor?

potench
  • 3,802
  • 1
  • 28
  • 39
  • Not sure if this helps but here's a jsfiddle link http://jsfiddle.net/potench/VMGVP/ The code results in an "object is not a function" error – potench Dec 28 '11 at 20:24

4 Answers4

5

You're confusing a couple of concepts. In your first example, MyModule doesn't have a constructor, it is a constructor. That is, it's a function object that you intend to use with the new operator to create new objects. Those objects will have a constructor property that points back to MyModule (the function that created them). Changing this constructor property won't have any effect on MyModule; that property is just a pointer back to the function that instantiated the object.

In other words, you can't change MyModule's constructor. That's a meaningless statement.

Now, when you write:

var MyModule = {};

...you create a new object whose constructor property is Object():

console.log(MyModule.constructor) // prints Object()

Again, changing this property doesn't really do much (except obfuscate some useful book-keeping).

At this point MyModule is just a plain-old object. It's not a function at all. That's why you're getting the "not a function" error. Because it's not, but you're trying to use it as though it is. If you want that name to refer to a function (i.e. to a different object) then you're going to lose all references to any properties you previously set, because you're pointing at an entirely new object.

That's just the way it is.

Now, you could save a reference to the object that contains all those previously-set properties and copy them back into MyObject once you've pointed that name at a function. But I'm not sure what the point would be.

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • Right, you can't meaningfully change `MyModule.constructor`... You should change `MyModule.prototype.constructor` instead. See my answer. – personal_cloud May 13 '22 at 23:42
2

Everything lwburk says is correct, however, the below does what you were trying to accomplish, it does this by calling an init() method from MyModule.

var MyModule = function(){
    return this.init();
};

MyModule.prototype.init = function(){};

MyModule.RelatedModule = (function() { // add properties to the MyModule object
    return function() {
       console.log("ran RelatedModule");
    };
}());

MyModule.prototype.init = function(){
   console.log("ran MyModule");
};

var inst1 = new MyModule(),
    inst2 = new MyModule.RelatedModule();
Taka
  • 21
  • 1
1

First, there is a known browser bug where the constructor property (of the constructed object) correctly resolves using the prototype, but is not applied in object construction. So you need the following polyfill:

function NEW(clas, ...args)
{
  let res = Object.setPrototypeOf({}, clas.prototype);
  res.constructor.apply(res, args);
  return res;
}

Second, you need to be setting MyModule.prototype.constructor instead of MyModule.constructor. The reason is that MyModule.constructor is the function that constructs new classes, not the function that constructs new objects. (See: Why does a class have a "constructor" field in JavaScript?)

In other words:

var MyModule = {}; // create object first, try to set a constructor on it later

MyModule.RelatedModule = (function() { // add properties to the MyModule object
    return function() {
        console.log("ran RelatedModule");
    }
}())

// set the `MyModule` prototype.constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.prototype = {
  constructor() {
      console.log("ran MyModule");
}}

var inst1 = NEW(MyModule), // logs "ran MyModule"
    inst2 = NEW(MyModule.RelatedModule); // logs "ran RelatedModule"
personal_cloud
  • 3,943
  • 3
  • 28
  • 38
0

The short answer is that you can't do that. You can however change the prototype of an object. Check out this answer for insight into how to rethink your approach to work with this constraint: Changing constructor in JavaScript

Community
  • 1
  • 1
mattacular
  • 1,849
  • 13
  • 17