Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closures: Equivalent of PHP "use" keyword or C++ capture list in Javascript or a transpiler language

in PHP we have the neat use keyword for allowing the usage of 'external' variables when using closures, like the following:

$tax = 10;
$totalPrice = function ($quantity, $price) use ($tax){  //mandatory 'use'
    return ($price * $quantity) * ($tax + 1.0);
};

If we omit the use ($tax) part, it will throw an error, which I like a lot.

Similarly in C++ 11, we do the same, specifying the external variables, called capture list, with brackets:

float tax = 10;
auto totalPrice = [tax](int quantity, float price){   //mandatory []
   return (price*quantity) * (tax + 1.0);
};

As in php, it will throw an error if the capture list is omitted.

In Javascript, we don't have an equivalent to this use keyword (or c++ []), we just do:

var tax = 10;
var totalPrice = function (quantity, price){  //no need for 'use' or similar
    return (price * quantity) * (tax + 1.0);  //tax is usable here :(
};

I don't like much that freedom, I strongly prefer to specify the variables that will be accessible by the closure function or get an error otherwise, for reasons outside the scope of this question.

So, I was wondering, is there a special keyword or operator for this in ES6, or in any language that transpiles to javascript? (CoffeeScript, TypeScript, etc) If so, in which language and what's the syntax?

Ideally I'd like to detect in transpilation time (or before), when a variable hasn't been explicitly 'authorized' to be used in a closure, pretty much like PHP/C++.

Thanks in advance

PS: Please don't ask me why I want this in js-like language, that debate is another topic.

EDIT: A linter that performs this check would also help

like image 898
Edgar Villegas Alvarado Avatar asked Jan 10 '16 02:01

Edgar Villegas Alvarado


People also ask

What is a closure function in JavaScript?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.

What is scope and closure in JavaScript?

JavaScript allows us to nest scopes, and variables declared in outer scopes are accessible from all inner ones. Variables can be globally-, module-, or block-scoped. A closure is a function enclosed with references to the variables in its outer scope.

Which of the following statements are correct regarding JavaScript closures?

Explanation: In JavaScript, closures are created every time a function is created, at function creation time. Technically, all JavaScript functions are closures: they are objects, and they have a scope chain associated with them.

Is closure an encapsulation?

A closure is an example of encapsulation: it encapsulates the body of code together with the lexical scope. The only means of access into the capsule is through the function: the function is like a "method", and the elements of the captured lexical environment are like "slots" in an object.


2 Answers

Unfortunately as you can immagine the keyword use doesn't exists in javascript but to achieve the result you are looking for there are several ways.

So this was your example in which totalPrice is a function and tax is global.

// Your example
var tax = 10;
var totalPrice = function (quantity, price) {
    return (price * quantity) * (tax + 1.0);
};
var price = totalPrice(1, 1);
console.log ("Price YE is : " + price);

So I think that probably the solution who simulate more the keyword use is to generate a function which initialise tax in a sub scope and return a function:

// Example 1 return function
function generatePriceCalculator(tax) {
    return function(quantity, price) {
        if ("undefined" === typeof tax) {
            throw "tax is undefined";
        }

        return (price * quantity) * (tax + 1.0);
    };
};

var priceCalculator = generatePriceCalculator(20);
var price1 = priceCalculator(1, 1);
console.log ("Price Example 1 is : " + price1);

As you can see generatePriceCalculator is setting the value for tax in the function that return.

Another option is to generate an external function to call inside the closure.

// Example 2 function return tax
function getTax() {
    return 30;
}
var totalPrice2 = function (quantity, price) {
    var tax = getTax();
    return (price * quantity) * (tax + 1.0);
};
var price2 = totalPrice2(1, 1);
console.log ("Price Example 2 is : " + price2);

You can see all of them here:

https://jsfiddle.net/jo9yzoke/1/

like image 65
borracciaBlu Avatar answered Nov 03 '22 15:11

borracciaBlu


There is no such keyword in JavaScript.

In JavaScript variables are available to all child scopes, for example:

(function() {
  var outerVariable = true;
  (function() {
    console.log(typeof outerVariable); // boolean
    (function() {
      console.log(typeof outerVariable); // boolean
    }())
  }());
}());

However, you can't access variables which has been defined in a separate, not-parent scope, for example:

(function() {
  var someVariable = true;
}());
(function() {
  console.log(typeof someVariable); // undefined
}());

This is the reason why you should write JavaScript code in such a way that you always have access only to variables that you need. Consider following example:

(function() {
  var math = (function() {
    // inner variable
    var PI = 3.141592653589793;

    // inner function
    function multiply(...args) {
      return args.reduce((a, b)=> a * b);
    }

    // outer functions
    return {
      circleArea: function circleArea(r) {
        return multiply(r, r, PI);
      },
      circumference: function circumference(r) {
        return multiply(2, PI, r);
      }
    };
  }());

  console.log(math.circleArea(5)); // 78.53981633974483
  console.log(math.circumference(10)); // 62.83185307179586

  console.log(typeof PI); // "undefined"
  console.log(typeof multiply); // "undefined"
}());

Inside the IIFE which creates the math object, you can use PI variable and multiply function. The IIFE returns two function, which can access PI and multiply too, because they are still inside this IIFE. You can call math.circleArea() and math.circumference() from outside this IIFE, but you can access PI or multiply — they are undefined in this scope.

See also:

  • How do JavaScript closures work?
  • What is the purpose of wrapping whole Javascript files in anonymous functions like “(function(){ … })()”?
like image 26
Michał Perłakowski Avatar answered Nov 03 '22 14:11

Michał Perłakowski