I'm only started to understand closures when suddenly it throws me another curveball, making my understanding of the scoping in closures even worse. I have a relatively simple closure just so I can get jQuery working inside of an object, which is then called by event handlers outside.
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
<div id='sidebar-overlay'></div>
<div id="sidebar"></div>
<script>
var cwn = (function($) {
var app = {
overlay: {
e: "#sidebar-overlay",
activate() {
$(this.e).fadeIn();
$('body').css('overflow', 'hidden');
},
deactivate() {
$(this.e).fadeOut();
$('body').css('overflow', 'unset');
},
},
sidebar: {
e: '#sidebar',
open() {
app.overlay.activate();
$(this.e).animate({"width": 'show'});
},
close() {
app.overlay.deactivate();
$(this.e).animate({"width": 'hide'}, 'fast');
}
}
};
return app;
})(jQuery);
<script>
So using it in the console itself or calling the function without the event handlers, results in it working - the sidebar activates and opens up and does what it's supposed to.
cwn.sidebar.open(); // THIS WORKS JUST FINE
However using this said function with an event handler results in this
changing.
$('.hamburger-link').on('click', cwn.sidebar.open); // THIS CHANGES 'this' TO SOMETHING ELSE
Which then causes it to fail.
I have an interim solution - which is to replace this.e
with app.sidebar.e
but that just seems extremely cumbersome and it just seems to me that there is a better and simpler solution out there.
CodePudding user response:
It's fairly widely understood that object initializers do not support the use of this
to refer to the object or properties within it during execution of the initializer. Refer to Self-references in object literals / initializers and the list of linked questions presented on page for a treatment of this.
This is not particularly related to the use of closures - more simply it's not supported by object initializer syntax.
However in this case, given a closure has already been set up by an IIFE, you could always define element selectors within the closure for use as constants within the app, for example:
var cwn = (function($) {
// selectors
const overlay= "#sidebar-overlay";
const sidebar = "#sidebar";
// app
const app = {
overlay: {
activate() {
$(overlay).fadeIn();
$('body').css('overflow', 'hidden');
},
deactivate() {
$(overlay).fadeOut();
$('body').css('overflow', 'unset');
},
},
sidebar: {
open() {
app.overlay.activate();
$(sidebar).animate({"width": 'show'});
},
close() {
app.overlay.deactivate();
$(sidebar).animate({"width": 'hide'}, 'fast');
}
}
};
return app;
})(jQuery);
There are of course alternatives, including binding functions within the object to the object they need to be called on. Even so I think it unlikely any particular solution will ever prove to be the best approach in all cases. Go for readability and maintainability when in doubt.