简体   繁体   中英

This changes when called by different functions? Javascript closure

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 class="hamburger-link">
   <div class="bar bar1"></div>
   <div class="bar bar2"></div>
   <div class="bar bar3"></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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM