IIFE
First, we use an IIEF because we want to invoke a function immediately. This puts the function onto the execution stack. We put parameters are global, and $ because we want to attach properties to the global variable. The $ is jQuery.
Hence, our function looks like this:
1 2 3 |
function(global, $) { } |
We want to execute it immediately so that it gets processed and data gets attached to the global variable. Hence we execute it as a function expression and pass in the window object, and jQuery.
1 2 3 4 |
(function(global, $) { }(window, jQuery)); |
Factory Function
We then create Greetr as a function where we use a utility function called Creator to return instances.
Say we do it this way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
(function(global){ // factory function that returns instances var Greetr = function(firstName, lastName, language) { return new Greetr.Creator(firstName, lastName, language); } Greetr.Creator = function(fName, lName, lang) { this.firstName = fName || ''; this.lastName = lName || ''; this.language = lang || 'en'; } }(window, jQuery)); |
We have a special Creator function that returns instances. However, take note that Greetr is a function with its own prototype object. Creator is a reference to a function. Creator has its own prototype object like this:
So when we get a new Greetr.Creator(…), the instances are pointing to Greetr.Creator’s prototype object.
Now, this is fine and dandy in situations if we were to implement prototypal functions/properties for our Creator prototype, and Greetr prototype separately.
But in our case, we just want to implement prototypal functions/properties for Greetr prototype only. We want all other functions to inherit from our Greetr. Thus, that’s is why we must point our Greetr.Creator’s prototype to Greetr.prototype. That way, any instances created from new Greetr.Creator will inherit from Greetr’s prototype object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
(function(global){ // factory function that returns instances var Greetr = function(firstName, lastName, language) { return new Greetr.Creator(firstName, lastName, language); } Greetr.Creator = function(fName, lName, lang) { this.firstName = fName || ''; this.lastName = lName || ''; this.language = lang || 'en'; } // when used with 'new', returns an Creator that // points to Greetr.prototype Greetr.Creator.prototype = Greetr.prototype; }(window, jQuery)); |
Add our Greetr function to the global object so we can use it
Finally, we attach our Greetr function as a property to the global object so we can use it like so:
1 |
let a = global.Greetr('ricky', 'cao'); |
Now, let’s show that our diagram and our code match up.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
(function(global){ // factory function that returns instances var Greetr = function(firstName, lastName, language) { return new Greetr.Creator(firstName, lastName, language); } // this is where we put our custom prototype properties and functions Greetr.prototype = { createdBy: 'ricky tsao', copyRight: '2019 Home' } Greetr.Creator = function(fName, lName, lang) { this.firstName = fName || ''; this.lastName = lName || ''; this.language = lang || 'en'; } // when used with 'new', returns an Creator that // points to Greetr.prototype Greetr.Creator.prototype = Greetr.prototype; // add property 'Greetr' to the global object, then have it point to // our factory function Greetr global.Greetr = Greetr; }(global)); let a = global.Greetr('ricky', 'cao'); // { firstName: 'ricky', lastName: 'cao', language: 'en' } console.log(a); // { createdBy: 'ricky tsao', copyRight: '2019 Home' } console.log(a.__proto__); // {} console.log(a.__proto__.__proto__); // [Function: Object] console.log(a.__proto__.__proto__.constructor); |
Adding functionality and exposing them
Add private variables
First we add private objects with properties en and zh. This is so that we can assoiative key/value access like so dictionary[“key”] –> value
We want to use it like this:
1 2 |
greetings['en'] formalGreetings['en'] |
then get the string phrases back.
Hence we do it like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
(function(global){ // other code // private variable to be used var supportedLangs = ['en', 'zh']; var greetings = { en: 'hello', zh: 'ni hao', }; var formalGreetings = { en: 'Greetings my friend', zh: 'Nin hao wo de peng you', }; // other code }(global)); |
Expose them publicly via prototype object
What we just declared has private access. We expose them by using them in Greetr’s prototype object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Greetr.prototype = { fullName: function() { return this.firstName + ' ' + this.lastName; }, // this.language by default is 'en'. And will be other language if set. // so we be sure that it will have a value validate: function() { if (supportedLangs.indexOf(this.language) === -1 ) { throw "Invalid language"; } else { console.log(`√ valid language`); } }, doGreeting: function() { return greetings[this.language] + ', ' + this.firstName; }, doFullGreeting: function() { return formalGreetings[this.language] + ', ' + this.fullName(); } } // privately declared objects |
This way, you are declaring function properties for prototype objects. And as we know, all instances and objects that inherit from our prototype objects can access its functionality. Therefore, when we do
1 |
new Greetr.Creator(...) |
we create instances that inherit from Greetr.prototype, and thus, can access its functions/properties
like so:
1 2 3 4 |
let a = global.Greetr('ricky', 'cao'); a.validate(); // √ valid language console.log(a.doGreeting()); // hello, ricky console.log(a.doFullGreeting()); // Greetings my friend, ricky cao |
Source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
(function(global){ // factory function that returns instances var Greetr = function(firstName, lastName, language) { return new Greetr.Creator(firstName, lastName, language); } // this is where we put our custom prototype properties and functions // we expose functionalities in this prototype object // we do so by declaring properties Greetr.prototype = { // placeholders to show exposure createdBy: 'ricky tsao', copyRight: '2019 Home', // our public functionality // return full name fullName: function() { return this.firstName + ' ' + this.lastName; }, // this.language by default is 'en'. And will be other language if set. // so we be sure that it will have a value validate: function() { if (supportedLangs.indexOf(this.language) === -1 ) { throw "Invalid language"; } else { console.log(`√ valid language`); } }, doGreeting: function() { return greetings[this.language] + ', ' + this.firstName; }, doFullGreeting: function() { return formalGreetings[this.language] + ', ' + this.fullName(); } } // private variable to be used var supportedLangs = ['en', 'zh']; var greetings = { en: 'hello', zh: 'ni hao', }; var formalGreetings = { en: 'Greetings my friend', zh: 'Nin hao wo de peng you', }; Greetr.Creator = function(fName, lName, lang) { this.firstName = fName || ''; this.lastName = lName || ''; this.language = lang || 'en'; } // when used with 'new', returns an Creator that // points to Greetr.prototype Greetr.Creator.prototype = Greetr.prototype; // add property 'Greetr' to the global object, then have it point to // our factory function Greetr global.Greetr = Greetr; }(global)); let a = global.Greetr('ricky', 'cao'); a.validate(); console.log(a.doGreeting()); console.log(a.doFullGreeting()); |
Chaining
We can also do chaining where we check the language, then just log all the greetings altogether like this:
1 |
a.setLang('zh').doGreeting().doFullGreeting(); |
Simply add the setLang function to update the this.language property, and then change doGreeting and doFullGreeting function where they return the ‘this’ reference. That way, you get the instance object back and then can call another function on that instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
setLang: function(newLanguage) { this.language = newLanguage; this.validate(); return this; }, doGreeting: function() { console.log(greetings[this.language] + ', ' + this.firstName); return this; }, doFullGreeting: function() { console.log(formalGreetings[this.language] + ', ' + this.fullName()); return this; } |