28

I'm reading the Javascript Guide from Mozilla And when they contrasted JS to Java , It got me thinking, Java code is easily split up with each class in his own file. after futher search , I understand that the same can be accomplished in JS with namespacing and module pattern - I messed around with it but got very confused ( especially with calling a constructor declared in File1.js into File2.js )

so here is the hierarchy:  class organization

But i just can't figure out how to properly make it works

how do i simply go from

//employe.js
function Employee () {
  this.name = "";
  this.dept = "general";
}

function Manager () {
  this.reports = [];
}
Manager.prototype = new Employee;

function WorkerBee () {
  this.projects = [];
}
WorkerBee.prototype = new Employee;

function SalesPerson () {
  this.dept = "sales";
  this.quota = 100;
}
SalesPerson.prototype = new WorkerBee;

to this :

 // employe.js
function Employee () {
  this.name = "";
  this.dept = "general";
}

 // Manager.js   
function Manager () {
  this.reports = [];
}
Manager.prototype = new Employee;

 // WorkerBee.js     
function WorkerBee () {
  this.projects = [];
}
WorkerBee.prototype = new Employee;

 // SalesPerson.js      
function SalesPerson () {
 this.dept = "sales";
 this.quota = 100; 
 }
SalesPerson.prototype = new WorkerBee;
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
MimiEAM
  • 2,505
  • 1
  • 26
  • 28
  • 2
    All you have to do is include the files in the correct order. – Felix Kling Oct 28 '12 at 02:04
  • @FelixKling: As the code only declares the constructor functions and doesn't create anything, the order that the files are included doesn't even matter. – Guffa Oct 28 '12 at 02:08
  • @Guffa: It does, they are creating an instance for prototype inheritance, e.g. `Manager.prototype = new Employee;`. – Felix Kling Oct 28 '12 at 02:12
  • @FelixKling: Oh, you are right. I stand corrected. They do need to be included in the right order. – Guffa Oct 28 '12 at 02:14
  • If using modern `class`es, I solved this problem like so: https://stackoverflow.com/a/62142995/1599699 – Andrew Jun 02 '20 at 00:29

3 Answers3

20

You should have one global namespacing object which every module has to access and write to. Modify your files like so:

// employe.js

window.myNameSpace = window.myNameSpace || { };

myNameSpace.Employee = function() {
    this.name = "";
    this.dept = "general";
};

and Manager.js could look like

// Manager.js

window.myNameSpace = window.myNameSpace || { };

myNameSpace.Manager = function() {
    this.reports = [];
}
myNameSpace.Manager.prototype = new myNameSpace.Employee;

This is of course a very simplified example. Because the order of loading files and dependencies is not child-play. There are some good librarys and patterns available, I recommend you looking at requireJS and AMD or CommonJS module patterns. http://requirejs.org/

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • this make perfect sense, thank you - I looked at requireJS but I didn't quite understood how it would help me here - I think I'm confusing between require php style and JS – MimiEAM Oct 28 '12 at 03:14
  • 1
    I'm guessing you added `window.myNameSpace = window.myNameSpace || { };` in each file to avoid any loading errors. Is that correct? – zachdyer May 27 '16 at 04:18
  • @zachdyer I'd agree. Just check how EaseIJS does: https://github.com/CreateJS/EaselJS/blob/master/src/easeljs/filters/BlurFilter.js, it does the same trick –  Nov 30 '16 at 10:25
8

You don't need to do anything differently. Just include the script files and they work as if it was a single file.

Javascript doesn't have file scope. Once the code is parsed it doesn't matter where the code came from.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 3
    True - but its whether good practice nor convenient. You should mention that this only works because the *global object* is the *variable object* for itself and hence, all *var* and *function* declarations are implicitly written to it. Clobbering the global object like so is still bad karma. – jAndy Oct 28 '12 at 02:13
  • 1
    @jAndy: Yes, you are right that keeping the identifiers out of the global scope is better practice, but it's not a step that is at all needed to put the code in separate files. – Guffa Oct 28 '12 at 02:17
  • @Guffa I keep reading that we should avoid "polluting" the global scope is there a other reason beside not exposing a variable that we do not want to modify ? – MimiEAM Oct 28 '12 at 03:19
  • 3
    @MimiEAM: Yes. When you use more than one script library in a page, the risk that their identifiers clash is less if they define less global identifiers. – Guffa Oct 28 '12 at 09:54
  • This is not true for JS classes today. – Andrew Jun 02 '20 at 00:09
5

For small and medium projects like a website or game, the native namespacing and constructors work very well. They are a poor choice when the loading order is too complex to handle without some sort of autoloading.

index.html:

<script src="Employee.js"></script>
<script src="Manager.js"></script>

Manager.js:

var Manager = function() {
    var employee1 = new window.Employee(this);
    var employee2 = new window.Employee(this);
};

Employee.js:

var Employee = function(boss) {
    // work stuff here
    this.wage = 5;
};

Note, properties inside the employee constructor function are visible to the manager. The new word signals a constructor. This is also possible without a constructor by returning an object with properties instead of a function as shown above.

Eddie
  • 1,428
  • 14
  • 24