Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: put prototype in var for better minification #9638

Closed
Malkiz opened this issue Jul 12, 2016 · 11 comments
Closed

Suggestion: put prototype in var for better minification #9638

Malkiz opened this issue Jul 12, 2016 · 11 comments
Labels
Help Wanted You can do this Suggestion An idea for TypeScript
Milestone

Comments

@Malkiz
Copy link

Malkiz commented Jul 12, 2016

I recently developed a module in TS, meant for the web, so it is also being minified.
I reviewed the output file and found that the word prototype is not minified (naturally, since it is a reserved word). But my module ended up having 153 occurrences of prototype, which amounted to 1.34 KB out of a total of 21KB in the minified file (I am using Google Closure Compiler with the ADVANCED_OPTIMIZATIONS flag).

So I had an idea that can reduce that by putting the prototype into a variable before using it in every class definition.

Example:

Code

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
    bla() {
        return this.greet() + this.greeting;
    }
    moreBla() {
        return this.greet() + this.greeting;
    }
    blablabla() {
        return this.greet() + this.greeting;
    }
}

class Hello extends Greeter {
    greet() {
        return "Goodbye";
    }
}

let greeter = new Greeter("world");

let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function() {
    alert(greeter.greet());
    var x = [greeter.bla(), greeter.moreBla(), greeter.blablabla()];
    console.log(x);
}

document.body.appendChild(button);

Actual behavior:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    Greeter.prototype.bla = function () {
        return this.greet() + this.greeting;
    };
    Greeter.prototype.moreBla = function () {
        return this.greet() + this.greeting;
    };
    Greeter.prototype.blablabla = function () {
        return this.greet() + this.greeting;
    };
    return Greeter;
}());
var Hello = (function (_super) {
    __extends(Hello, _super);
    function Hello() {
        _super.apply(this, arguments);
    }
    Hello.prototype.greet = function () {
        return "Goodbye";
    };
    return Hello;
}(Greeter));
var greeter = new Greeter("world");
var button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function () {
    alert(greeter.greet());
    var x = [greeter.bla(), greeter.moreBla(), greeter.blablabla()];
    console.log(x);
};
document.body.appendChild(button);

Suggested behavior:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    var p = Greeter.prototype;
    p.greet = function () {
        return "Hello, " + this.greeting;
    };
    p.bla = function () {
        return this.greet() + this.greeting;
    };
    p.moreBla = function () {
        return this.greet() + this.greeting;
    };
    p.blablabla = function () {
        return this.greet() + this.greeting;
    };
    return Greeter;
}());
var Hello = (function (_super) {
    __extends(Hello, _super);
    function Hello() {
        _super.apply(this, arguments);
    }
    var p = Hello.prototype;
    p.greet = function () {
        return "Goodbye";
    };
    return Hello;
}(Greeter));
var greeter = new Greeter("world");
var button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function () {
    alert(greeter.greet());
    var x = [greeter.bla(), greeter.moreBla(), greeter.blablabla()];
    console.log(x);
};
document.body.appendChild(button);
@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Jul 12, 2016
@RyanCavanaugh RyanCavanaugh changed the title Suggestion: put prototype in var for better obfuscation Suggestion: put prototype in var for better minification Jul 12, 2016
@RyanCavanaugh
Copy link
Member

Updating title to "minification" since that's usually what people call it when optimizing for download size

@evmar
Copy link
Contributor

evmar commented Jul 12, 2016

This sounds like it could be a suggestion for the Closure compiler. It does these sorts of rewrites in other circumstances, so perhaps there's a good reason it doesn't do it here.

@Malkiz
Copy link
Author

Malkiz commented Jul 12, 2016

@evmar I thought of that, but since Closure Compiler is not the only minification tool I figured it might be better to change the TS output itself. I can switch minification tools every day, but converting my code to a different language is a lot less likely to happen.

@ivogabe
Copy link
Contributor

ivogabe commented Jul 13, 2016

I'm not sure whether this would really reduce the gzipped output size. After gzipping, the string 'prototype' is only stored once. It would be interesting to see some numbers on this, maybe someone can create, well, a prototype 😅 and compare the numbers.

@RyanCavanaugh
Copy link
Member

Ungzipped size still matters for parse speed, but that's of course much harder to measure deterministically

@kitsonk
Copy link
Contributor

kitsonk commented Jul 14, 2016

Minifiers (Closure/Uglify/etc.) will typically not mangle property names, because it doesn't know which are "safe". Then there are property names that are "magical" like prototype which they will never mangle. Therefore the only reasonable way to allow a minifier to shorten such things is to create a reference to it that can be mangled.

My personal opinion is that down-emitting in the fashion suggested above would not have any negative consequences and doesn't make the down emit unclear (except maybe instead of the terse p it would be more clear to use something like var proto = Class.prototype which is more readable and would be mangled easily by a minifier if desired.

@jeffreymorlan
Copy link
Contributor

The output for subclasses could be shrunk further if __extends returned the prototype it creates.
Before: __extends(Hello, _super); var p = Hello.prototype;
After: var p = __extends(Hello, _super);

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this and removed In Discussion Not yet reached consensus labels Sep 19, 2016
@RyanCavanaugh RyanCavanaugh added this to the Community milestone Sep 19, 2016
@RyanCavanaugh
Copy link
Member

Accepting PRs. Be sure to handle name collisions (e.g. a reference to an outer p in the method bodies needs to not be shadowed).

@evmar
Copy link
Contributor

evmar commented Nov 16, 2022

This bug was filed in 2016, which is possibly before Closure Compiler supported ES6 classes natively. Today, I think the right way to solve this problem is to have TS and Closure emit ES6, or if you must downlevel you could downlevel with Closure.
So from that perspective, I think you could close this bug. I don't think optimizing ES5 output is worth worrying about these days.

@GeneGenie you can change the target emit to ES6 in the playground to fix your output too.

@RyanCavanaugh
Copy link
Member

Yeah, I agree this isn't worth any additional effort at this point.

@RyanCavanaugh RyanCavanaugh closed this as not planned Won't fix, can't repro, duplicate, stale Nov 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
7 participants