Prevent JavaScript closure from inheriting scope
Asked Answered
L

2

8

I am looking for a fancy way to prevent a closure from inheriting surrounding scrope. For example:

let foo = function(t){

  let x = 'y';

  t.bar = function(){

    console.log(x); // => 'y'

  });

};

there are only two ways I know of preventing sharing scope:

(1) Use shadow variables:

let foo = function(t){

  let x = 'y';

  t.bar = function(x){

    console.log(x); // => '?'

  });

};

(2) Put the function body somewhere else:

  let foo = function(t){

      let x = 'y';

      t.bar = createBar();

    };

My question is - does anyone know of a 3rd way to prevent closures from inheriting scope in JS? Something fancy is fine.

The only thing that I think could possibly work is vm.runInThisContext() in Node.js.

Let's use our imaginations for a second, and imagine JS had a private keyword, which meant the variable was private only to that function's scope, like this:

  let foo = function(t){

      private let x = 'y';  // "private" means inaccessible to enclosed functions

      t.bar = function(){

        console.log(x); // => undefined

      });

    };

and IIFE won't work:

let foo = function(t){

    (function() {
    let x = 'y';
    }());

   console.log(x); // undefined (or error will be thrown)
   // I want x defined here

  t.bar = function(){
    // but I do not want x defined here
    console.log(x); 
  }

  return t;
};
Licastro answered 22/7, 2017 at 23:45 Comment(17)
There generally isn't much use for "private" variables or variables that aren't accessible in a lower scope etc. that why there really aren't any ways to create such variables. If you want something to not be accessible, enclose it in it's own scope, such as an IIFE etc.Kristopher
Maybe you want an IIFE?Jeannettejeannie
show me how an IIFE solves this problem and I will show you $1000 lolLicastro
Why would you want foo to create that bar of t in first place?Njord
(or 10 SO points)Licastro
it's just a simple example, demonstrating a closure.Licastro
May I ask your use case?Jeannettejeannie
@AlexanderMills Is there another good reason why you do need the closure and you just want to make certain scope variables "private" to it?Njord
@KyleRichardson it's a test harness with nested blocks, in some cases it would be convenient to prevent child blocks from inheriting the parent block's scope, to avoid sharing data, etc.Licastro
@Njord I want to make variables in the parent scope inaccessible to the child (closure) scope.Licastro
You can transfer my 1000 bucks to my account in the Caymans -> jsfiddle.net/01fyqp0vKristopher
@Kristopher thanks, I believe I need the full link, I went straight to jsfiddle.net, no further path.Licastro
@Kristopher fine work, but there problems with the IIFE approach, I addressed it in question.Licastro
Got it, block scope seems to be the way!Jeannettejeannie
What an intriguing question, the OP must be quite dashing indeedLicastro
@AlexanderMills I dunno... I think the OP is kinda IIFE.Jeannettejeannie
lol thanks I will take it as a compliment :)Licastro
L
1

This technique works:

Create helper function to run a function in an isolated scope

 const foo = 3;

 it.cb(isolated(h => {
    console.log(foo);  // this will throw "ReferenceError: foo is not defined"
    h.ctn();
 }));

you might also have some luck with the JavaScript with operator

Licastro answered 2/1, 2018 at 21:16 Comment(0)
S
8

You can use block scope

let foo = function(t) {
  {
    // `x` is only defined as `"y"` here
    let x = "y";
  } 
  {
    t.bar = function(x) {
      console.log(x); // `undefined` or `x` passed as parameter
    };
  }
};


const o = {};
foo(o);

o.bar();
Selia answered 23/7, 2017 at 0:5 Comment(19)
well I will be damnedLicastro
already upvoted, plus I was awarding $1000 usd only for working IIFE solution :) (although this might use an IIFE under the hood). any other fancy ways you can think of @guest271314?Licastro
I had an IIFE solution to your pre-updated use! This is a great solution though.Jeannettejeannie
There are probably other patterns that can meet requirement. Though why try to determine a process which would necessitate more code than four characters?Selia
This is the clear winner for me, and I previously advocated IIFE.Jeannettejeannie
@Selia this is for a library not an application so not necessarily looking for most terse solution.Licastro
@AlexanderMills Did not ask what the use case is, only attempted to meet requirement. Whether used within a "library" or "application" is immaterial. The original Question specifically asks for one further optional solution "My question is - does anyone know of a 3rd way". Why attempt to include yet another requirement at comments?Selia
If you're writing a library, then you're probably doing it wrong if you need this to make it work. This is however a good answer, block scope works great with let and const which are of course block scoped.Kristopher
@Kristopher well, unfortunately the JS language itself does not do a great job of keeping "private" data private...as a library author I sometimes have to jump through hoops to keep private data inaccessible etc.Licastro
@Selia I was the first or second to upvote your answer - I didn't add another requirement, I just was wondering if you could think of any other way to solve the problem besides block scope. Also, as a request, if you could comment (for everyone's sake) on the availability of block scope in browsers and Node.js, that always helps.Licastro
Block statements by themselves are supported in almost all browsers, and have been a part of JS since the beginning, with and without labels etc. However block scoped variables, aka let and const are only available in newer browsers, and this won't work with a regular var, which is function scoped.Kristopher
Should be noted that if you wanted it to work in older browsers, with var, we're back to the extra function to create a new scope, most likely an IIFE again, which would look eerily similar to a block statement but with more characters -> jsfiddle.net/adeneo/ydpfaq5w/1Kristopher
@Kristopher yes I think that block scope uses IIFE under the hood, it's most likely just syntactic sugar amigo...but TBH the IIFE is fugly and block scope is passable.Licastro
@Selia why so literal? You know that by the 3rd way, I meant 3rd or 4th or 5th :)Licastro
No they don't use an IIFE internally, then they'd create "function scope", which they don't. Again, blocks have been a part of JS since the beginning, and aren't functions, but they haven't been very useful until block scoped variables became available.Kristopher
@AlexanderMills "why so literal?" For clarity as to requirement.Selia
we might be able to use with in JS, see: #21016306Licastro
and: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Licastro
I added an answer that works pretty well, using new Function()Licastro
L
1

This technique works:

Create helper function to run a function in an isolated scope

 const foo = 3;

 it.cb(isolated(h => {
    console.log(foo);  // this will throw "ReferenceError: foo is not defined"
    h.ctn();
 }));

you might also have some luck with the JavaScript with operator

Licastro answered 2/1, 2018 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.