13

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

Edgar Villegas Alvarado
  • 18,204
  • 2
  • 42
  • 61
  • I don't think there's a transpiler that could do that. It would have to manage the scope of these functions and that could very well break your code. It'd be a very finicky business. I think your best bet would be to have a linter that warns you about your current closures. – MinusFour Jan 10 '16 at 02:35
  • @MinusFour a linter or similar would be great, do you know one that checks this particular case? – Edgar Villegas Alvarado Jan 10 '16 at 02:39
  • Sadly, I don't. But it's definitely doable... It's not a feature many will go for because we use them all the time and it'd be bothersome to have the linter scream at us everytime we use one. It'd be a nice thing to have though as a super strict flag (IMO there are more ridiculous rules than this one). – MinusFour Jan 10 '16 at 02:47
  • JSLint already covers this (`X was used before it was defined`), unless the closure is already reachable from around the function definition (like a wrapping private), in which case it won't bother you about it. – dandavis Jan 19 '16 at 04:16
  • Thanks MinusFour, dandavis. @dandavis, unfortunately, my problem is with defined variables, so that lint rule wouldn't be useful. – Edgar Villegas Alvarado Jan 19 '16 at 23:53
  • you might be able to code your own sweet.js macros instead of using a full-blown transpiler. you can also use run-time checks and unit tests to enforce such a coding style. – dandavis Jan 20 '16 at 00:05
  • the problem is that sweet.js won't give me any warning when an external var is used, it will only translate to js. Having errors detected at compile time would save tons of unit tests (or would make them much shorter). – Edgar Villegas Alvarado Jan 26 '16 at 20:34

6 Answers6

3

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/

borracciaBlu
  • 4,017
  • 3
  • 33
  • 41
  • Thanks for the answer. Unfortunately it does not prevent/warn outer vars from being used inside the closure. I think it would be only possible with a transpiler language or linter, I was expecting that kind of answer. – Edgar Villegas Alvarado Jan 19 '16 at 23:40
2

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:

Community
  • 1
  • 1
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
  • Thanks for the answer, I know all this about closures, unfortunately this does not solve my problem. If it's not possible with javascript, I was expecting a solution with transpiler/linter – Edgar Villegas Alvarado Jan 19 '16 at 23:42
  • @EdgarVillegasAlvarado There is no solution to this because of how JavaScript works. "JS is not PHP nor C++ and wont ever be." You just can't write your JavaScript in the same way you write PHP or C++. – Michał Perłakowski Jan 19 '16 at 23:59
  • That's why I mentioned transpiler language. If it's not possible in JS I expected a solution with transpiler language, which is theoretically possible. – Edgar Villegas Alvarado Jan 20 '16 at 00:00
  • @EdgarVillegasAlvarado But even transpiler JS languages follow basic JS logic. – Michał Perłakowski Jan 20 '16 at 00:02
  • But in a transpiler language, let's say coffeescript-like, it would be possible to get an error at transpiling time when an outer var has been used without "permission", as in several other transpiler constructs, that's what I need. The resulting javascript doesn't matter much. Actually I plan to add this feature to a transpiler, but first I wanted to know if it already exists. – Edgar Villegas Alvarado Jan 20 '16 at 00:08
  • Don't get me wrong, I appreciate your answer, and it has cool concepts. Thank you very much, +1 – Edgar Villegas Alvarado Jan 20 '16 at 00:12
2

You could bind your external variables to the this keyword of your closure, using the .bind() function:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

2

Here's another alternative approach. If your goal in looking for an equivalent of use in javascript is to write cleaner code, you might want to take a look at window.variablename. Variables at the global scope in javascript are implicitly properties of the window object. So for example, you could write:

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

This makes it explicitly clear to someone reading the code, that tax referenced in the function is the tax that's part of the global scope (namely window.tax).

VKK
  • 882
  • 7
  • 19
  • Although this doesn't solve my problem, for my purposes it's a good idea that outer variables are used through an intermediate object (I think window isn't a good one, but could be another), it opened me for new ideas, +1 for you, thanks – Edgar Villegas Alvarado Jan 19 '16 at 23:49
1

First of all: JS is not PHP nor C++ and wont ever be.

use() is not for JS. You have lots of possible examples to do what you need, it'a pretty flexible language. Except those in previous answers, I'd like to share one more example:

var tax = 'improper';

var priceFactory = function(tax) { 
    // private `tax` variable for this scope
    var tax = tax;

    // or a nightmare to check it
    // var tax = 'number' ==  typeof tax && tax || function() { throw "No tax given"; }();

    return function(quantity, price) {
        return (price * quantity) * (1 + tax / 100);
    }
};

var price = new priceFactory(10); // 10%

var totalPrice = price(1, 100)
yergo
  • 4,761
  • 2
  • 19
  • 41
  • Thanks for the answer. I know all this, I'm experienced with JS. Unfortunately this does not solve my problem. If it's not possible with javascript, I was expecting a solution with transpiler/linter. – Edgar Villegas Alvarado Jan 19 '16 at 23:44
0

I don't think JS will ever offer what you want. PHP has a strict scope. If I define a variable inside a function it's limited to that function. Likewise, something defined outside that function is not implicitly available inside the function. use, like global simply changes where and when you can use a variable inside a specific scope.

JS puts everything in the global scope by default. In fact, it's a form of bizarro world, in that you limit a variable's scope to within a function you either have to have it as an argument or explicitly limit the scope to that variable

function bob(x) {
    // x is implicitly local
    y = 1; //global
    var z = 2; //local
}

In other words, all variables in JS are use declared implicitly.

Machavity
  • 30,841
  • 27
  • 92
  • 100