Repository navigation
Suggestion: put prototype in var for better minification #9638
Copy link
Copy link
Closed as not planned
Labels
Milestone
Description
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);Reactions are currently unavailable
Activity
[-]Suggestion: put prototype in var for better obfuscation[/-][+]Suggestion: put prototype in var for better minification[/+]RyanCavanaugh commentedon Jul 12, 2016
Updating title to "minification" since that's usually what people call it when optimizing for download size
evmar commentedon 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 commentedon Jul 12, 2016
Evan Martin (@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 commentedon 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 commentedon Jul 13, 2016
Ungzipped size still matters for parse speed, but that's of course much harder to measure deterministically
kitsonk commentedon 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
prototypewhich 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
pit would be more clear to use something likevar proto = Class.prototypewhich is more readable and would be mangled easily by a minifier if desired.jeffreymorlan commentedon Aug 29, 2016
The output for subclasses could be shrunk further if
__extendsreturned the prototype it creates.Before:
__extends(Hello, _super); var p = Hello.prototype;After:
var p = __extends(Hello, _super);RyanCavanaugh commentedon Sep 19, 2016
Accepting PRs. Be sure to handle name collisions (e.g. a reference to an outer
pin the method bodies needs to not be shadowed).GeneGenie commentedon Nov 16, 2022
anybody knows why TS compiles protos "great" again in 2022?
https://www.typescriptlang.org/play?target=1&jsx=0&module=5&ts=3.3.3#code/AQYwNghgzlwGYHsHAN4FgBQxvAA4FcAjMASxGEIgCcAKASlUx2eAjAFMqAXGgIkqq86AbiY4AvmOwFiZChABe9RlhbY2nHv0VDRq7OJyZJGTOGiwBwdgA8u7AHYATWImTp982nQ9r1HbhoARjopYBMTTEwaB3YAdy96OgA6AXogA
Ryan Cavanaugh (@RyanCavanaugh)
evmar commentedon 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.
Gena (@GeneGenie) you can change the target emit to ES6 in the playground to fix your output too.
RyanCavanaugh commentedon Nov 16, 2022
Yeah, I agree this isn't worth any additional effort at this point.