Home > Enterprise >  This changes when called by different functions? Javascript closure
This changes when called by different functions? Javascript closure

Time:04-11

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.

  • Related