Uglifyjs: [ES6] convert anonymous function expressions to shorter arrow functions

Created on 23 Jun 2017  路  18Comments  路  Source: mishoo/UglifyJS

This is a proof of concept patch to convert anonymous function expressions to shorter arrow functions:

--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4539,6 +4539,18 @@ merge(Compressor.prototype, {
         return self;
     });

+    OPT(AST_Function, function(self, compressor){
+        // function(x,y){x+y;} --> (x,y)=>{x+y;}
+        if (compressor.option("ecma") >= 6
+            && !self.name && !self.uses_arguments && !self.is_generator && !self.async) {
+            return make_node(AST_Arrow, self, {
+                argnames: self.argnames,
+                body: self.body,
+            });
+        }
+        return self;
+    });
+
     OPT(AST_Arrow, function(self, compressor){
         if (self.body.length === 1 && self.body[0] instanceof AST_Return) {
             var value = self.body[0].value;

It works in basic cases such as the following:

$ echo 'var f = function(x){return x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6
var f=x=>{return x*x};console.log(f(7));
$ echo 'var f = function foo(x){return x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6,passes=2
var f=x=>{return x*x};console.log(f(7));



md5-52ad3f70dbf07923f056a4a5df17fe2f



$ echo 'var f = function foo(x){return x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6,passes=3
ERROR: Cannot read property 'each' of undefined



md5-6e07a9a7469598a17d4ed6d0731958c5



$ echo 'var f = function foo(x){return x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6,passes=3,reduce_vars=0
var f=x=>x*x;console.log(f(7));



md5-ea4223ea6b75d27cbeba9c0ffffbcfd3



$ bin/uglifyjs --self -mc ecma=6,passes=3,reduce_vars=0
ERROR: Cannot read property 'get' of undefined



md5-f131102b13897afa7bcff679ba909b06



$ echo 'var f = async function foo(x){return x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6,passes=3,reduce_vars=0
var f=async function(x){return x*x};console.log(f(7));



md5-ed9f14dbcb04eb33ed1bbc5618df1766



$ echo 'var f = function* foo(x){yield x * x;}; console.log(f(7));' | bin/uglifyjs -c ecma=6,passes=3,reduce_vars=0 
var f=function*(x){yield x*x};console.log(f(7));
enhancement harmony

Most helpful comment

If the code ain't broke - why fix it?

You & I both fail as software developers for the 21st Century, that's why :wink:

All 18 comments

IIRC arrow functions don't have proper scopes, i.e. you are use this directly within an arrow function instead of var self = this; in the normal anonymous function cases.

So there is no arrow function equivalent for this:

var o = {
    f: function() {
        this.a = 1;
    }
};
o.f();
console.log(o);

IIRC arrow functions don't have proper scopes, i.e. you are use this directly within an arrow function instead of var self = this; in the normal anonymous function cases.

So are we in agreement to exclude converting such function expressions that contain this?

Also, I think arrow functions cause significant runtime performance regressions in some JavaScript engine(s). We need to make sure those have already been taken care of, otherwise the uglification would bring about some nasty surprises.

So are we in agreement to exclude converting such function expressions that contain this?

:+1:

OTOH, the (arg) => value form would extend return elimination beyond the constraints of IIFE & constant value.

Also, I think arrow functions cause significant runtime performance regressions in some JavaScript engine(s).

First I've heard of that but anything's possible. I propose an opt-in behind the -c emca=6 option. Or we can give it an explicit option: -c prefer_arrows.

OTOH, the (arg) => value form would extend return elimination beyond the constraints of IIFE & constant value.

That arrow return optimization is already in uglify-es.

First I've heard of that but anything's possible.

IIRC some people was trying to "modernise" Node.js, and got held back due to such regressions.

That arrow return optimization is already in uglify-es

Cool :+1:

IIRC some people was trying to "modernise" Node.js, and got held back due to such regressions.

I see. If the code ain't broke - why fix it?

If the code ain't broke - why fix it?

You & I both fail as software developers for the 21st Century, that's why :wink:

Was looking around for #2102 & found http://2ality.com/2016/10/async-function-tips.html#arrayprototypemap

This does not work, because await is syntactically illegal inside normal arrow functions.

async function downloadContent(urls) {
    return urls.map(url => {
        // Wrong syntax!
        const content = await httpGet(url);
        return content;
    });
}

So one more thing to scan for and avoid.

So one more thing to scan for and avoid.

It's already handled by uglify-es:

Parse error at 0:4,30
        const content = await httpGet(url);
                              ^
ERROR: Unexpected token: name (httpGet)

By the way, the !self.uses_arguments in the patch is due to:

No binding of arguments

Arrow functions do not bind an arguments object. Thus, in this example, arguments is simply a reference to the same name in the enclosing scope

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

@kzc yup - that was one of the reasons why I refrain from using this syntactic sugar.

We also need to check for self.uses_eval because, you know, people love eval("arguments") :smiling_imp:

(Working on this as we speak...)

We also need to check for self.uses_eval because, you know, people love eval("arguments")

Good idea - might as well check for its obligatory partner in crime, self.uses_with, just in case.

How do you break this using with? I ask because optimisations like function substitution only checks for uses_arguments and uses_eval at the moment, so they may need to be fixed if with also breaks their assumption of safety.

BTW, this feature would break unsafe_Func :angry:

How do you break this using with?

I don't even try to attempt to analyze the implications of optimizing with statements after being burned by it numerous times in the past. If it's seen - just shut down all optimization.

Was this page helpful?
0 / 5 - 0 ratings