使用对象属性重写进行优化

 

ppt @ google doc

 

ppt @ slideshare


 

概念:


lifesinger 处看到了关于兼容性的讨论,早先也有过类似的想法 ,其中提到了对象重写,恰好最近也频繁用到,这里对一个实例进行讲解下。所谓对象属性重写(特别是方法),即在运行时动态修改对象的属性以及方法,属于脚本语言所特有,如文中兼容层处理,在运行中对方法进行重写改变。

 

var obj = {
    run: function () { //action3
    }
};
if (condition) {
    obj.run = function { //action2
    };
}

 

实例优化 step by step:


常见的浮动窗口实现。


1.最简单的类:

 

function Dialog() {
    this.el = new Node("<div style='display:none;position:absolute;left:100px;top:100px'>我是窗口</div>")
    document.body.appendChild(this.el[0]);
}
Dialog.prototype = {
    constructor: Dialog,
    show: function () {
        this.el.css("display", "");
    },
    hide: function () {
        this.el.css("display", "none");
    }
};


缺点:产成实例后即生成 div 节点,如果用户用不到(永远不弹出),则白白消耗了宝贵的 dom 操作,下面即照着这个思路进行优化



2.利用标志,on-demand 创建节点

 

function Dialog_v2() {

}

Dialog_v2.prototype = {
    constructor: Dialog_v2,
    show: function () {
        if (!this.init) {
            this.el = new Node("<div style='display:none;position:absolute;left:100px;top:100px'>我是窗口</div>")
            document.body.appendChild(this.el[0]);
            this.init = true;
        }
        this.el.css("display", "");
    },
    hide: function () {
        this.el.css("display", "none");
    }
};

 

静态语言的思路,增加标志 (this.init) 来控制初始化的次数,只有在第一次显示时才进行创建。(这里也可以判断 this.el 的存在与否,道理相同)



3.直观的对象方法重写

 

function Dialog_v3() {

}

Dialog_v3.prototype = {
    constructor: Dialog_v3,
    show: function () {

        this.el = new Node("<div style='display:none;position:absolute;left:100px;top:100px'>我是窗口</div>")
        document.body.appendChild(this.el[0]);

        var self = this;
        self.show = function () {
            self.el.css("display", "");
        };
        self.show();
    },
    hide: function () {
        this.el.css("display", "none");
    }
};
   


缺点:1.不够分离与自动化,每次都得在原函数中进行操作,和原有逻辑混在一起。

        2.增加了内存占用,在实例自身中创建函数,取代了原型链查询,但也同时提升了访问速度。

        3.若用户开始就注册 this.show 到 dom 节点事件,则后面重写无效。



4.分离自动化的的对象方法重写

 

步鄹详解:

 

1.分离初始化动作(_prepare) 与实际动作(_real),利用lazyRun公共机制串联。

2.lazyRun在实例生成后立即重写 _prepare。 调用 _prepare 时,先调用自身,再调用实际操作代码_real,最后用 _real 重写自己。

3.不修改对外接口 show,show 中直接调用 _prepare,可用它做为事件回调函数。

 

 

 

/*
  核心机制,串联 obj[before] 与 obj[after],并在第一次执行后替换 
obj[before] 为 obj[after]
*/
var lazyRun = function (obj, before, after) {
    var b = obj[before],
        a = obj[after];
    obj[before] = function () {
        b.apply(this, arguments);
        a.apply(this, arguments);
        obj[before] = obj[after];
    };

};

function Dialog_v4() {
    lazyRun(this, "_prepare", "_real");
}
Dialog_v4.prototype = {
    constructor: Dialog_v4,
    show: function () {
        this._prepare();
    },
    _real: function () {
        this.el.css("display", "");
    },
    _prepare: function () {
        this.el = new Node("<div style='display:none;position:absolute;left:100px;top:100px'>我是窗口</div>");
        document.body.appendChild(this.el[0]);
    },
    hide: function () {
        this.el.css("display", "none");
    }
};

 

 

5.进阶的原型方法重写

 

在 4  中我们进行了实例方法的重写,实际是将方法从构造器原型移到了对象实例,而实例所属构造器的原型并没有发生变化,这一步尝试进行构造器原型方法的重写,达到影响该构造器所有实例的目的。

 

场景:


还是弹窗,但是常常伴随着遮罩层(mask),遮罩层是所有 dialog 实例公用,但是如果一开始就创建遮罩层,就显得有点浪费(实例还没生成呢),所有我们常常这样处理:

 

 

function Mask() {}

function Dialog_v5() {}
Dialog_v5.prototype = {
    prototype: Dialog_v5,
    show: function () {
        if (!Dialog_v5.mask) {
            Dialog_v5.mask = new Mask();
        }
        //use mask
    }
};
 

按照优化4 的思路,直接重写原型方法:

 

function Dialog_v6() {

}

Dialog_v6.prototype = {
    prototype: Dialog_v6,
    _prepare: function () {
        Dialog_v6.mask = new Mask();
    },
    _real: function () {
        //use mask
    },
    show: function () {
        this._prepare();
    }
};
lazyRun(Dialog_v6.prototype, "_prepare", "_real");
 

这样的话当某个实例第一次执行 show 时会运行 _prepare 而初始化 mask,随之构造器的原型立即被更改,则以后所有的弹窗实例 show 时都会直接运行构造器原型上被覆盖后的 _prepare 即 _real,达到了 所有实例共享 mask 的目的。

 

 

 

总结:


提升效率有很多方面,对于web页面(特别是首页)开发来说就是加快初屏载入速度(减少startup latency ,提升 perceptive speed),一个有效方法就是尽可能得将资源延迟到它真正需要的时候才分配,例如宏观方面的 datalazyloader 。PS:和这篇文章 中的 “You can often cut the script-portion of your page load time the most by simply redesigning your code to do less work up front . defers execution whenever possible. ”有相似之处。

 

而在代码的微观粒度,如果为了达到这个目的而盲目得添加 if 绝不是明智之举,通过本文的方法重写,不但可以真正将初始化逻辑与使用逻辑分离,而且节省了条件判断,初始化后将自身重定向到真正的业务处理逻辑,天然的保证了初始化的单一性。


至于是要重写实例方法还是构造器原型方法,则要仔细分析资源的所有权,若资源是属于实例所有,如窗口的 dom 节点,则要重写实例方法,若资源属于类(构造器),需要在所有的实例间共享,如所有窗口的公共遮罩层,那么这种情形下更适合直接重写构造器原型。

 

 

修订于:2010-08-12

你可能感兴趣的:(css,prototype,Google,脚本,UP)