JS一些类实现方式的性能研究

更新日期: 2020-04-08阅读: 1.6k标签: 作者: 司徒正美

从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,免去每次都往上层的作用域寻找。


实现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类的成员很小,内部私有属性也很少,性能明显比以上所有实现要好得多。


链接: https://www.fly63.com/article/detial/8551

es6之js的类

ES6 的类让 JS 中的继承变得更简单,因此对于你已从其他语言学习到的类知识,你无须将其丢弃。 ES6 的类起初是作为 ES5 传统继承模型的语法糖,但添加了许多特性来减少错误。

利用 es6 new.target 来对模拟抽象类

最近在使用 Symbol 来做为唯一值,发现 Symbol 无法进行 new 操作,只能当作函数使用,只要进行了new 就会发生类型错误,在不考虑底层实现的情况下,在代码层面是否能够实现一个函数只可以进行调用而不可以进行 new 操作呢?

Js即将加入私有字段和私有方法

尽管 JavaScript 在 2015 年就有了类,但仍然没有私有字段和私有方法。由于 TC39 委员会 内部存在分歧,这些功能在最初版本中被取消。有三个规范草案打算在不久的将来将这些功能引入到 JavaScript 类中。

ts中类的定义

类里面的修饰符 typescript里面定义属性的时候给我们提供了 三种修饰符public :公有 在当前类里面、 子类 、类外面都可以访问,protected:保护类型 在当前类里面、子类里面可以访问 ,在类外部没法访问

创建Vue类的过程

构造函数、全局配置对象、默认options配置、比如 vue-router 就会注册这个回调,因此会每一个组件继承,前面提到了,默认组件有三个 `KeepAlive`,`transition`, `transitionGroup`

javascript中uber实现子类访问父类成员

上面代码通过实例化子类和父类,分别调用toString()实现了继承的关系。这个时候有这样的需求;不实例化父类,直接通过子类完完整整的调用父类的方法或属性。

如何在Vue中动态添加类名

能够向组件添加动态类名是非常强大的功能。它使我们可以更轻松地编写自定义主题,根据组件的状态添加类,还可以编写依赖于样式的组件的不同变体。添加动态类名与在组件中添加 prop :一样简单。

了解JavaScript中的类

javascript是一种基于原型的语言,javascript中的每个对象都有一个名为[[原型]]的隐藏内部属性,可用于扩展对象属性和方法。直到最近,勤奋的开发人员使用构造函数来模仿JavaScript中面向对象的设计模式。

javascript如何定义类?

JavaScript中没有类或接口的概念,即不能直接定义抽象的类,也不能直接实现继承。不过,为了编程的方便,我们可以在JavaScript中模拟类和继承的行为。

在Vue中如何避免在动态绑定类出现空类的情况?

传递空字符串,这可能会导致 DOM 输出中的类为空。 在三元运算符中,我们可以返回null,这可以确保 DOM 中没有空类

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!