JavaScript Object Inheritance with IIFE?

663 views Asked by At

I won't lie I'm awesome with JavaScript nor that I understood the IIFE concept in its totality, but since I read jQuery Best Practices and then first met the concept plus some researching made later, I came up to this:

( function( MyObject, window, document, $, undefined ) {

    var privateVariable = {};

    MyObject.publicVariable;

    function privateMethod() {}

    myObject.publicMethod() {}

}( window.MyObject = window.MyObject || {}, window, document, window.jQuery ) );

Although it works nicely as expected I recently felt the need of some form of abstraction so i could create a base class with shared properties and methods and child objects with their own logic.

That way I wouldn't need, let's say, repeat a getter common to all children in each individual object.

I've searched about this matter, of course, but I didn't find specific information on how to implement such feature with this concept.

2

There are 2 answers

4
phnkha On

How about using a construction method:

( function( MyObject, window, document, $, baseClass) {

    // calling base class for common properties, methods
    baseClass && baseClass.call(MyObject);

    var privateVariable = {};

    MyObject.publicVariable;

    function privateMethod() {}

    myObject.publicMethod() {}

}( window.MyObject = window.MyObject || {}, window, document, window.jQuery, baseClass ) );
0
James Sumners On

The only thing the IIFE is providing you is an enclosed scope. You do not need multiple IIFEs, unless there is some reason you have not stated. So, first, let's cover what the "best practices" link you read is really suggesting.

In the browser environment, all JavaScript is executed within a single scope. Thus, the following two scripts will have the same scope despite being different files:

script_a.js

var foo = 'bar'
console.log('foo = ', foo)

script_b.js

if (foo === 'bar') {
  console.log('foo is still bar')
}

But if we wrap each script in an IIFE, the scope changes and script_b will no longer function:

script_a.js

(function ()  {
  var foo = 'bar'
  console.log('foo = ', foo)
}())

script_b.js

(function () {
  if (foo === 'bar') { // throws an error because `foo` is not defined
    console.log('foo is still bar')
  }
}())

Given the nature of web development, and jQuery with plugins, many scripts get loaded and the global scope gets polluted. This can result in hard to identify side effects. Therefore it is recommended that you contain your code within its own scope. This is accomplished with the IIFE since functions have their own local scope and the function is invoked on load.

Based on your example, it looks like you merely want composable objects so that you can keep them separated in individual files during development. This is a clear usage of the module pattern:

script_c.js

var MY_NAMESPACE = (function () {
  var privateFoo = 'foo'

  return {
    getFoo: function getFoo () {
      return privateFoo
    }
  }
}())

script_d.js

(function (NAMESPACE) {
  var privateBar = 'bar'

  NAMESPACE.getBar = function getBar () { return privateBar }
}(MY_NAMESPACE))

script_e.js

console.log(MY_NAMESPACE.getFoo()) // 'foo'
console.log(MY_NAMESPACE.getBar()) // 'bar'

Notice that MY_NAMESPACE is a global variable. This is where you have to be unique, as it is susceptible to being overwritten by some later loaded script that could overwrite your global variable.

I haven't used it, but there is a library called stampit that expands upon this pattern and makes it simple to use.

With all of that said, you can then extend your MY_NAMESPACE object into a new object:

script_f.js

var CHILD_NAMESPACE = Object.create(MY_NAMESPACE)

CHILD_NAMESPACE.fooOrBar = (function (NS) {
  var counter = 0
  return function () {
    var answer = (counter % 2 === 0) ? NS.getFoo() : NS.getBar()
    counter += 1
    return answer
  }
}(CHILD_NAMESPACE))

Of course, all of the above scripts could be contained within a single overall IFFE to completely contain your code within its own scope.