Better Compression with UglifyJS

By  on  

UglifyJS is widely known as the most performant and effective JavaScript minifier available.  UglifyJS' default minification with --compress is nice but it doesn't do the full job.  There are a number of additional directives for the compress option, including:

  • sequences -- join consecutive simple statements using the comma operator
  • properties -- rewrite property access using the dot notation, for example foo["bar"] → foo.bar
  • dead_code -- remove unreachable code
  • drop_debugger -- remove debugger; statements
  • unsafe (default: false) -- apply "unsafe" transformations (discussion below)
  • conditionals -- apply optimizations for if-s and conditional expressions
  • comparisons -- apply certain optimizations to binary nodes, for example: !(a <= b) → a > b (only when unsafe), attempts to negate binary nodes, e.g. a = !b && !c && !d && !e → a=!(b||c||d||e) etc.
  • evaluate -- attempt to evaluate constant expressions
  • booleans -- various optimizations for boolean context, for example !!a ? b : c → a ? b : c
  • loops -- optimizations for do, while and for loops when we can statically determine the condition
  • unused -- drop unreferenced functions and variables
  • hoist_funs -- hoist function declarations
  • hoist_vars (default: false) -- hoist var declarations (this is false by default because it seems to increase the size of the output in general)
  • if_return -- optimizations for if/return and if/continue
  • join_vars -- join consecutive var statements
  • cascade -- small optimization for sequences, transform x, x into x and x = something(), x into x = something()
  • warnings -- display warnings when dropping unreachable code or unused declarations etc.
  • negate_iife -- negate "Immediately-Called Function Expressions" where the return value is discarded, to avoid the parens that the code generator would insert.
  • pure_getters -- the default is false. If you pass true for this, UglifyJS will assume that object property access (e.g. foo.bar or foo["bar"]) doesn't have any side effects.
  • pure_funcs -- default null. You can pass an array of names and UglifyJS will assume that those functions do not produce side effects. DANGER: will not check if the name is redefined in scope. An example case here, for instance var q = Math.floor(a/b). If variable q is not used elsewhere, UglifyJS will drop it, but will still keep the Math.floor(a/b), not knowing what it does. You can pass pure_funcs: [ 'Math.floor' ] to let it know that this function won't produce any side effect, in which case the whole statement would get discarded. The current implementation adds some overhead (compression will be slower).
  • drop_console -- default false. Pass true to discard calls to console.* functions.

So instead of simply doing a basic compress, squeeze the hell out of your JavaScript files by altering booleans, removing unneeded var uses, axing unreachable code, and much more.  Here's an example of such a case using the NodeJS API:

var UglifyJS = require('uglify-js');
var fs = require('fs');

var result = UglifyJS.minify('site.js', {
	mangle: true,
	compress: {
		sequences: true,
		dead_code: true,
		conditionals: true,
		booleans: true,
		unused: true,
		if_return: true,
		join_vars: true,
		drop_console: true
	}
});

fs.writeFileSync('site.min.js', result.code);

You can pass those compression values via command line as well.  This post isn't meant to be groundbreaking but more to raise awareness that simply using --compress doesn't optimize minification anywhere near potential.  If you're going to minify and compress your JavaScript, go all out!

Recent Features

  • By
    Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

  • By
    9 Mind-Blowing WebGL Demos

    As much as developers now loathe Flash, we're still playing a bit of catch up to natively duplicate the animation capabilities that Adobe's old technology provided us.  Of course we have canvas, an awesome technology, one which I highlighted 9 mind-blowing demos.  Another technology available...

Incredible Demos

  • By
    MooTools TwitterGitter Plugin

    Everyone loves Twitter. Everyone loves MooTools. That's why everyone should love TwitterGitter, a MooTools plugin that retrieves a user's recent tweets and allows the user to format them however the user would like. TwitterGitter allows the user to choose the number of...

  • By
    Scroll IFRAMEs on iOS

    For the longest time, developers were frustrated by elements with overflow not being scrollable within the page of iOS Safari.  For my blog it was particularly frustrating because I display my demos in sandboxed IFRAMEs on top of the article itself, so as to not affect my site's...

Discussion

  1. Petah

    I thought closure compiler was the most effective

    • Jason

      It probably is in Advanced mode.

    • It really is; it’s just slower and inconvenient to run because it’s Java. Uglify’s most awesome feature is that it’s easy to deploy, run, and bundle. And while it compresses poorly, it’s safer than CC’s Advanced mode because it doesn’t optimize as much.

  2. kurtextrem

    You could also use “screw_ie8” (in case you don’t support ie8). Looks like this in grunt:

    uglify: {
    	options: {
    		mangle: {
    			//toplevel: true,
    			screw_ie8: true
    
    		},
    		compress: {
    			screw_ie8: true,
    			sequences: true,
    			//properties: true,
    			dead_code: true,
    			drop_debugger: true,
    			comparisons: true,
    			conditionals: true,
    			evaluate: true,
    			booleans: true,
    			loops: true,
    			unused: true,
    			hoist_funs: true,
    			if_return: true,
    			join_vars: true,
    			cascade: true,
    			//negate_iife: true,
    			drop_console: true
    		}
    	},
    	main: {
    		src: ['src/main.js'],
    		dest: 'dist/main.min.js'
    	}
    },
    
  3. eugene

    Most of these options are on by default – they are there to disable them

  4. Bunt

    “slower and inconvenient to run because it’s Java”

    Java is faster than node. If it’s inconvenient, it’s because it’s badly written or you are a scriptkiddie

    • Anon

      >Java is faster than node
      Sure thing, buddy. Now go and run Closure Compiler and UglifyJS, one of them feels like opening Photoshop, which one is it? The Java one or the Node one?

      Yeah. Turns out a decades-old language that didn’t improve much and is owned by Oracle isn’t that great.

    • Nona

      They mean faster than node when it comes to the code running. Java is slower to load because of the VM. Node is slower to execute because of the need to go through a JS interpreter. It’s not black and white though, and there are some areas where Node runs faster.

  5. Rods

    You could also avoid minify some functions.
    Maybe if you have some unobtrusive function [onclick="functionName01"].
    Not in the docs.

    mangle: {
        except: ['functionName01', 'functionName02'],
        screw_ie8: true
    }
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!