ADJenks
ADJenks

Reputation: 3424

Get variable in scope without closure / wrapping function

If I have a variable that is changing over time and I want to preserve a particular instance of it I have to wrap it in a closure like so:

function test(){
	var x = Math.random();
	// Is there an alternative to using the following closure:
	var printnum = (function(num){ 
		return function(){
			console.log(num);
		}
	})(this.x); // Because I think this is ugly
	
	return printnum;
}

var a = test();
var b = test();

a(); //number
a(); //same as above
b(); //different number
b(); //same as above

In PHP you would use use like so:

$a = rand();
function () use ($a) {
  echo($a);
}

I really appreciate this because you immediately see which variable is being injected, it's not listed at the very bottom like in js: (function(b){})(a); Also there aren't an excessive amount of brackets. I tried experimenting with .apply() and .call() and but they both execute the function, I just want to inject a variable in a certain state.

I think I'm just asking for a language feature that doesn't exist, but please prove me wrong. Any ideas?

Upvotes: 0

Views: 130

Answers (4)

dandavis
dandavis

Reputation: 16726

a simple way using .bind() (ES5) to remove the wrapper functions and avoid closure:

function test(){
  return console.log.bind(console, Math.random());
}

Upvotes: 2

Barmar
Barmar

Reputation: 781761

There's no need for the printnum function. Just return the inner anonymous function.

This is essentially the same as the PHP, but it doesn't need the use() declaration to list variables to inherit into the closure (PHP needs this because the default is to not allow any access to outside variables).

function test() {
  var x = Math.random();
  return function() {
    console.log(x);
  }
}

var a = test();
var b = test();

a(); //number
a(); //same as above
b(); //different number
b(); //same as above

Upvotes: 1

Scott Marcus
Scott Marcus

Reputation: 65835

I personally like the IIFE, but "One man's garbage..." Anyway, you really just need to pass a copy of the number so that you don't get caught up in the closure variable.

function test(){
  // Because you are working with a number and JavaScript passes all arguments by value
  // a copy of the number will be passed to helper, allowing helper to "disconnect"
  // from using the number in the parent scope
  return helper(Math.random()); 
  
  function helper(num){
    return function(){ console.log(num); }
  }
}

var a = test();
var b = test();

a(); //number
a(); //same as above
b(); //different number
b(); //same as above

Upvotes: 1

CRice
CRice

Reputation: 32196

Can you use es6? If so, an arrow function will give the same result, but is more concise:

function test(){
	var x = Math.random();
  
	var printnum = x => () => console.log(x);
	return printnum(x);
  
    // OR you can invoke immediately:
    // var printnum = (x => () => console.log(x))(x);
    // return printnum;
}

var a = test();
var b = test();

a(); //number
a(); //same as above
b(); //different number
b(); //same as above

AFAIK, in javascript, you'll have to pass the x into the function one way or another to avoid it printing something else if x's value is later changed.

Upvotes: 1

Related Questions