JS一些類實現方式的效能研究
從javaeye看到一貼,探討如何實現計時器,集思廣益,最後竟然提出了十多種的實現。這再次證明了js的寫法很靈活(舉個反面的例子,如Python,其哲學原則是one way to go!)。這裡整理一下,研究一下各種實現的效能問題。現在js越來越向富客戶端發展,UI元件乃至網頁遊戲,對效能問題是相當重視的。
實現1
function Timer(id){
this.id = id;
this.begin = function(count){
this.show(this.id, count)();
setInterval(this.show(this.id, count-1),1000);
}
this.show = function(id, count){
return function(){
document.getElementById(id).innerhtml = count < 0 ? "over" :count;
count--;
}
}
}
點評:採用最經典的建構函式來實現類。好處是明快易懂,缺點是多個例項都是獨立的記憶體,資料與方法都不共享。而且還存在一個明顯的缺陷,計時完成之後,setInterval沒有被clear,嚴重影響了效能。
實現2
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
this.begin = function(count){
this.count = count;
this.show(this)();
this.timer = setInterval(this.show(this),1000);
}
this.show = function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerhtml = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
}
點評:解決了實現1的setInterval未被clear的問題,但依然是利用建構函式來實現類,因此Timer每個例項都複製了一份show和begin,而不是共享同一份。
實現3
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
this.begin = function(count){
this.count = count;
Timer.show(this)();
this.timer = setInterval(Timer.show(this),1000);
}
Timer.show = function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerHTML = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
}
點評:把show()弄成類的靜態方法,讓所有例項共享此方法。注意,show()是個currying函式,這就解決了各例項的資料互相汙染的問題。
實現4
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
this.begin = function(count){
this.count = count;
this.show(this)();//注意和實現三的區別:這裡不是Timer.show(this)();
this.timer = setInterval(this.show(this),1000);//注意和實現三的區別:這裡不是Timer.show(this)();
}
}
Timer.prototype.show = function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerHTML = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
點評:這次把show()做成原型方法,由於所有例項都是共享原型,當例項在其成員中找不到此方法,它就會沿著原型鏈往上找。這個運用了很經典的類實現方式——混合的建構函式 /原型方式。
實現5
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
}
Timer.prototype.begin = function(count){
this.count = count;
this.show(this)();//注意這裡不是Timer.show(this)();
this.timer = setInterval(this.show(this),1000);//注意這裡不是Timer.show(this)();
}
Timer.prototype.show = function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerHTML = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
點評:這次把bigin()也做成原型方法。
實現6
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
}
Timer.prototype = {
begin : function(count){
this.count = count;
this.show(this)();//注意這裡不是Timer.show(this)();
this.timer = setInterval(this.show(this),1000);//注意這裡不是Timer.show(this)();
},
show : function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerHTML = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
}
點評:把原型方法整到一個物件中去。
實現7
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
Timer.prototype.begin = function(count){
this.count = count;
this.show(this)();//主要這裡不是Timer.show(this)();
this.timer = setInterval(this.show(this),1000);//主要這裡不是Timer.show(this)();
}
Timer.prototype.show = function(obj){
return function(){
if(obj.count < 0){
document.getElementById(obj.id).innerHTML = "over";
clearInterval(obj.timer);
return ;
}
document.getElementById(obj.id).innerHTML = obj.count;
obj.count--;
}
}
}
點評:把原型方法整到建構函式中去,但千萬不要弄成這個樣子:
function Timer(id){
this.id = id;
this.timer = null;
this.count = 0;
Timer.prototype= {
begin:function(count){/*……*/},
show : function(obj){ /*……*/}
}
}
因為直接為prototype 賦值,會使得自動新增的constructor 成員丟失!
實現8
var Timer = {
begin : function(id,count){
var obj = {};
obj["id"] = id;
obj["count"] = count;
Timer.show(obj)();
obj["timer"] = setInterval(Timer.show(obj),1000);//注意這裡不是Timer.show(this)();
},
show : function(obj){
return function(){
if(obj["count"] < 0){
document.getElementById(obj["id"]).innerHTML = "over";
clearInterval(obj["timer"]);
return ;
}
document.getElementById(obj["id"]).innerHTML = obj["count"] ;
obj["count"]--;
}
}
}
點評:這次採用字面量構造物件。Timer成了一個全域性物件(當我們第一次呼叫begin方法才產生此全域性物件),類似於window,生成例項方法不再是new,而是採用工廠方式的方法,每begin一次就生成一個例項,資料是獨立的,方法是共享的。
實現9
var Timer = (function(){
var items = {};
function begin(id,count){
var obj = {};
obj["id"] = id;
obj["count"] = count;
Timer.show(obj)();
obj["timer"] = setInterval(Timer.show(obj),1000);//注意這裡不是Timer.show(this)();
Timer.items[id] = obj;
};
function show(obj){
return function(){
if(obj["count"] < 0){
document.getElementById(obj["id"]).innerHTML = "over";
clearInterval(obj["timer"]);
return ;
}
document.getElementById(obj["id"]).innerHTML = obj["count"] ;
obj["count"]--;
}
}
return {
items : items,
begin : begin,
show : show
}
})()
點評:利用閉包,和上面的有點類似,但此全域性變數是DOM樹完成後就立即生成的,它擁有以下三個方法items : begin 與 show。許多類庫也採用此方式實現,不過它們稱之為名稱空間而已……
實現10
function Timer(id){
this.element = document.getElementById(id);
this.timer = null;
this.count = 0;
}
Timer.prototype = {
begin : function(count){
this.count = count;
this.show();
var _this = this;
this.timer = setInterval(function(){_this.show();}, 1000);
}
,
show : function(){
this.element.innerHTML = this.count < 0 ? clearInterval(this.timer) || "over" : this.count--;
}
}
點評:這是實現3的改進版,改進了每呼叫一次show就遍歷DOM樹的缺點(document.getElementById),而是第一次找到的元素放入例項的成員。利用三元表示式與短路運算使得程式碼更加內斂。儲存this,免去每次都往上層的作用域尋找。
廣州vi設計http://www.maiqicn.com 辦公資源網站大全https://www.wode007.com
實現11
function Timer(id) {
this.container = document.getElementById(id);
}
Timer.prototype = {
constructor: Timer,
begin: function(count) {
var container = this.container;
setTimeout(function() {
container.innerHTML = count > 0 ? count-- : "over";
if(count + 1) {
setTimeout(arguments.callee, 1000);
}
}, 1000);
}
};
點評:YUI風格的實現方式,該類庫大量使用arguments ,callee等內部屬性。現在Timer類的成員很小,內部私有屬性也很少,效能明顯比以上所有實現要好得多。