GCC(Google Closure Compiler)是由谷歌發佈的Js代碼壓縮編譯工具。它能夠作到分析Js的代碼,移除不須要的代碼(dead code),而且去重寫它,最後再進行壓縮。javascript
GCC提供三種壓縮模式:css
1)Whitespace only 2)Simple 3)Advanced
咱們以這段簡單的代碼爲例前端
function sayHello(name) { alert('Hello, ' + name); } sayHello('binnng');
分別使用這三種壓縮模式進行壓縮:java
function sayHello(name){alert("Hello, "+name)}sayHello("binnng");
發現只是簡單的去除空格換行註釋。ajax
function sayHello(a){alert("Hello, "+a)}sayHello("binnng");
比Whitespace only
要高端一點,在其基礎上,還對局部變量的變量名進行縮短。這也是其餘壓縮工具所使用的壓縮方式,如Uglify等,也是最爲主流的壓縮方式。比較安全。json
alert("Hello, binnng");
會發現,Advanced
級別的壓縮改變(破壞)了原有代碼結構,直接輸出了代碼最終運行結果,可見的確是分析
,重寫
,破壞
,可是對代碼壓縮作到了極致,極大的減小了代碼量。segmentfault
正由於GCC是這樣的破壞性壓縮工具,因此使用起來得異常當心,稍微不規範可能就會引發壓縮報錯或者壓縮成功後卻不能正常運行。那麼,若是要使用Advanced
級別壓縮,要注意哪些呢?後端
如下全部未指名級別的GCC壓縮均爲Advanced
級別。api
var data = { user: "binnng", age: "18" }; if ("user" in data) { alert(data.age); }
通過Uglify壓縮:安全
var data={user:"binnng",age:"18"};"user"in data&&alert(data.age);
通過GCC壓縮:
var a={b:"binnng",a:"18"};"user"in a&&alert(a.a);
會發現GCC壓縮時,將變量的屬性名也縮短,代碼量減小了,可是卻帶來了問題,會發現以上GCC壓縮後的代碼運行實際上是不正確的。由於屬性名縮短改變後,data已經再也不擁有名爲user
的屬性了。
那如何解決呢?
var data = { user: "binnng", age: "18" }; if (data.user) { alert(data.age); }
這時候再通過GCC壓縮:
alert("18");
直接輸出了正確的結果。
若是不想讓GCC壓縮屬性名,好比在Ajax請求發送給後端接口的時候,能夠將屬性名用雙引號包裹:
var data = { "user": "binnng", "age": "18" }; var ajax = function(url, data, callback) { (new window.XMLHttpRequest()).send(data); // ... }; ajax("//baidu.com", data, function(res) {console.log(res)});
這樣通過GCC壓縮後:
(new window.XMLHttpRequest).send({user:"binnng",age:"18"});
原封不動的保留了後端須要的參數名。
var foo = "1234"; alert(widnow.foo);
這時候,通過GCC壓縮後:
alert(window.a);
有點莫名其妙。。由於在GCC中,再也不承認隱式全局變量,因此上面的代碼中在GCC眼裏,foo
是沒有掛載到window下的,因此下文的window.foo
實際上是未定義的。
要解決這個問題,必須將foo
顯式掛載到window下:
window.foo = "1234"; alert(widnow.foo);
這樣通過壓縮後:
window.a="1234";alert(widnow.a);
這時候可能就會疑問,爲什麼不直接壓縮成alert("1234")
呢?這是由於GCC不會去除掛載在window下的變量。
window.btnClick = function() { alert("clicked"); };
以上的代碼通過壓縮後成爲:
window.a=function(){alert("clicked")};
此時,若是HTML中存在以下代碼,就會報錯。
<a onclick="btnClick()">點我</a>
這時候就須要導出函數:
window["btnClick"] = function() { alert("clicked"); };
用雙引號包裹後,btnClick
就保留了下來。在構造函數中,尤爲須要注意導出,例如:
var Animal = function(name) { this.name = name; }; Animal.prototype.eat = function() { alert(this.name + " is eating!"); };
以上的代碼通過GCC壓縮後,什麼都沒剩下,由於GCC認爲,代碼中的代碼都沒有執行,屬於dead code
。既然這麼寫了,那確定是須要它,提供給別人外部調用什麼的,這時候就須要這麼導出:
var Animal = function(name) { this.name = name; }; Animal.prototype.eat = function() { alert(this.name + " is eating!"); }; window["Animal"] = Animal; Animal.prototype["eat"]= Animal.prototype.eat;
通過壓縮後:
function a(b){this.name=b}a.prototype.a=function(){alert(this.name+" is eating!")};window.Animal=a;a.prototype.eat=a.prototype.a;
這時候,Animal
這個方法成功的保留了下來。
還有,在使用JSONP
方式獲取服務端數據的時候,也必定要導出callback方法名:
var jsonpCb = function() { //... }; getScript("/api/data?callback=jsonpCb"); // 導出,不然jsonpCb會被壓縮掉不能被識別 window["jsonpCb"] = jsonpCb;
a.js
var getName = function() {return "binnng"};
b.js
alert(getName());
若是單獨壓縮a.js
和b.js
就會出問題,結果會是a.js
中什麼都沒有,b.js
中getName
方法未定義(undefined
)。正確的作法則是,二者合併再進行壓縮。
GCC的高級壓縮(Advanced)很是強大,對代碼壓縮作到了極致,可是其對代碼書寫要求也比較嚴格,而且破壞性壓縮也被不少開發者所詬病。
可是稍加註意,嚴格規範自身代碼風格,瞭解GCC壓縮方式原理,利用好GCC高級壓縮,必定會大大減小JS的體積,從而大幅度的提高前端代碼性能。
另外,GCC像其餘壓縮工具同樣,也有Grunt
、Gulp
構建組件,能夠很方便的去使用它。
//原文地址:http://segmentfault.com/blog/laopopo/1190000002575760