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));
AST_Function that has any AST_This child nodes - either directly or indirectly. The meaning of this differs in function expressions and arrow functions. See: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functionsIIRC 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.
Most helpful comment
You & I both fail as software developers for the 21st Century, that's why :wink: