I have a giant JavaScript monolith and used a module based approach to make it more structured.
However, I'm a bit lost on how to use the "this" keyword for nested functions, without overwriting each other, like the example below might demonstrate
function Fruit() {
const self = this;
this.fruits = [];
this.addFruit = function (fruit) {
self.fruits.push(fruit);
};
// Buy locally
this.localStore = (function () {
const self_localStore = this;
this.buyArray = [];
this.buy = function (fruit) {
console.log("Bought locally");
self_localStore.buyArray = this;
};
return this;
})();
// Buy from online store
this.onlineStore = (function () {
const self_onlineStore = this;
this.buyArray = [];
this.buy = function (fruit) {
console.log("Bought online");
self_onlineStore.buyArray = this;
};
return this;
})();
return this;
}
let fruit = new Fruit();
fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally
The localStore.buy()
method is for obvious reasons overwritten by the onlineStore.buy()
method, as they both are returned to the Fruit() methods scope, use the same name and the last one overwrites the first one.
So, what would be the solution to allow "namespaces" like onlineStore and localStore without using an object that has these nested methods?
When you write an IIFE, this
equals window
or undefined
(depending on whether you use strict mode or not):
(function() { console.log(this === window); })() // True
The this
keyword is being assigned the object from which the function is called, for example:
const obj = {
fun: function() { console.log(this === obj); }
}
obj.fun(); // True
If you want to create some namespace which has its own state, I suggest you to do it with a simple object like this:
function Fruit() {
const self = this;
this.fruits = [];
this.addFruit = function (fruit) {
self.fruits.push(fruit);
};
// Buy locally
this.localStore = {
buyArray: [],
buy: function (fruit) {
this.buyArray.push(fruit);
console.log("Bought locally:", this.buyArray.join(', '));
}
};
// Buy from online store
this.onlineStore = {
buyArray: [],
buy: function (fruit) {
this.buyArray.push(fruit);
console.log("Bought online:", this.buyArray.join(', '));
}
};
return this;
}
let fruit = new Fruit();
fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally
fruit.localStore.buy("banana");
fruit.onlineStore.buy("orange");
However, you may not like the literal objects, because they don't provide real encapsulation (the buyArray
property can be accessed from outside of the buy
function).
So you could also encapsulate the state in an IIFE like you initially tried to do:
function Fruit() {
const self = this;
this.fruits = [];
this.addFruit = function (fruit) {
self.fruits.push(fruit);
};
// Buy locally
this.localStore = (function() {
const buyArray = [];
const buy = function (fruit) {
buyArray.push(fruit);
console.log("Bought locally:", buyArray.join(', '));
};
return { buy };
})();
// Buy from online store
this.onlineStore = (function() {
const buyArray = [];
const buy = function (fruit) {
buyArray.push(fruit);
console.log("Bought online:", buyArray.join(', '));
};
return { buy };
})();
return this;
}
let fruit = new Fruit();
fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally
fruit.localStore.buy("banana");
fruit.onlineStore.buy("orange");