Search code examples
javascriptecmascript-6closuresdestructuring

Closures & Destructuring — Destructured functions can't call each other?


I have something like the following:

let MM = {
    a: function(){
        b();
    },
    b: function(){
        console.log('b');
    }
};

function makeClosure(M) {
    let { a, b } = M;

    function a2(){ b2() };
    function b2(){ console.log('b2'); };

    return function( arg ){
        if ( arg === 1 ) a();
        if ( arg === 2 ) a2();
    }
}

let c = makeClosure( MM );

////////////////////////////////////////////
// RESULTS

c( 1 ); // b() doesn't exist
c( 2 ); // b2() exists just fine

let MM = {
  a: function(){
    b();
  },
  b: function(){
    console.log('b');
  }
};

function makeClosure(M) {
    let { a, b } = M;

    function a2(){ b2() };
    function b2(){ console.log('b2'); };

    return function( arg ){
        if ( arg === 1 ) a();
        if ( arg === 2 ) a2();
    }
}

let c = makeClosure( MM );

////////////////////////////////////////////
// RESULTS

c( 1 ); // b() doesn't exist
c( 2 ); // b2() exists just fine

Why do the above Results outcomes happen? I thought destructuring was supposed to be equivalent to declaration. Why is the destructured b not found by a? They exist in the same scope?

Is there any way to make this work? I would prefer to destructure for organizational reasons. I can't keep them as methods for various reasons.


Solution

  • There is no standalone function named b from the lexical scope of MM.a, so calling a() from makeClosure results in an error because no variable or function named b can be found from within the a function in MM.

    One possibility would be to pass a the function to execute, that way MM doesn't rely on any outer variable:

    let MM = {
        a: function(someFn){
            someFn();
        },
        b: function(){
            console.log('b');
        }
    };
    
    function makeClosure(M) {
        let { a, b } = M;
    
        function a2(){ b2() };
        function b2(){ console.log('b2'); };
    
        return function( arg ){
            if ( arg === 1 ) a(b);
            if ( arg === 2 ) a2();
        }
    }
    
    let c = makeClosure( MM );
    c(1);
    c(2);

    Another option would be to call a inside makeClosure with a calling context of an object which has a b property, just like MM, and have MM.a call this.b:

    let MM = {
        a: function(){
            this.b();
        },
        b: function(){
            console.log('b');
        }
    };
    
    function makeClosure(M) {
        let { a, b } = M;
    
        function a2(){ b2() };
        function b2(){ console.log('b2'); };
    
        return function( arg ){
            if ( arg === 1 ) a.call({ b });
            if ( arg === 2 ) a2();
        }
    }
    
    let c = makeClosure( MM );
    c(1);
    c(2);

    The issue doesn't have anything to do with destructuring - it's just plain JS scoping rules.