`
yiminghe
  • 浏览: 1460380 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多

 

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

分享到:
评论
9 楼 yiminghe 2010-08-18  
zhangle1987 写道
提一个问题:最后一个例子里面,有多个dialog实例怎么办?原型在创建第一个实例的时候已经被改变了?

应该被改变,因为所有实例共享mask遮罩层的
8 楼 zhangle1987 2010-08-18  
提一个问题:最后一个例子里面,有多个dialog实例怎么办?原型在创建第一个实例的时候已经被改变了?
7 楼 yiminghe 2010-08-16  
十三‘’ 写道
是这个意思吗


最后一个例子是重写构造器原型,

lazyRun不在原型方法中,在外边


当某个实例第一次运行show时,导致其构造器原型被重写,其它实例再show时就会跳过mask初始化了
6 楼 十三‘’ 2010-08-16  
是这个意思吗
5 楼 十三‘’ 2010-08-16  
最好一个例子咋没明白啥意思呢
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"); 

lazyRun怎么在第一次实例化时起作用?

function Dialog_v6() {

}

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



4 楼 cloudgamer 2010-08-16  
我的意思是说这样可能每个public方法都要做一个判断,程序会变复杂
当然这个看怎么权衡了
这种方法感觉不利于扩展
例如在扩展或继承中想在show前加一段程序就出问题了
3 楼 yiminghe 2010-08-16  
cloudgamer 写道
那如果我先调用hide呢
那hide是不是也加一个_prepare
这样会增加很多复杂度整个程序会变得很难维护


为什么要先调用hide,未创建或刚创建出来都是和hide状态一样的,如果要处理的话就是:

hide:function(){
  this.el&&this.el.css("display","none");
}
2 楼 cloudgamer 2010-08-16  
那如果我先调用hide呢
那hide是不是也加一个_prepare
这样会增加很多复杂度整个程序会变得很难维护
1 楼 lifesinger 2010-08-11  
最后一个例子很精彩,列入下期分享,嘿嘿

相关推荐

    c#重写的tabcontrol控件

    在这个方法里,你可以使用Graphics对象绘制自己的控件样式,例如改变选中标签的颜色、形状、边框等。同时,可以利用DrawToBitmap方法生成预览图,用于实现平滑的动画效果。 2. **扩展事件处理**:原生TabControl的...

    面向对象设计 5 —— PHP5面向对象进阶(重写(override))

    在面向对象编程中,子类可以重写父类的方法,以便实现不同的功能或优化已有行为。这被称为方法的重写。在PHP5中,当子类定义了一个与父类同名且签名相同的方法时,就实现了重写。例如,`animal.php`可能包含一个基本...

    Python-使用解析表达式语法重写源代码

    理解这些节点类型及其属性是使用解析表达式语法重写源代码的基础。 **三、解析与生成源代码** 1. **解析(Parse)**: 使用`ast.parse()`函数可以将Python源代码字符串转换为AST对象。 2. **遍历(Traversal)**: `...

    重写MenuStrip

    标签"重写MenuStrip C#"强调了这是使用C#语言实现的,C#是.NET框架的主要开发语言之一,具有强大的面向对象特性和丰富的库支持。"好看的MenuStrip"暗示了这个控件在设计时注重了界面的美观性,可能包含了更现代的...

    重写TreeViewC#源码

    3. 自定义绘制:为了实现自定义的节点样式,可以重写`OnPaint`方法,使用`Graphics`对象进行绘制。这允许我们自由控制节点的颜色、形状、图标等。 4. 添加新功能:根据需求,可以添加新的属性、方法和事件。例如,...

    重写DataGridColumn

    3. **自定义单元格渲染**:为了实现更复杂的显示效果,如边框、图案填充等,我们需要重写`OnPaint`方法,使用`Graphics`对象进行绘制。这可能涉及到一些GDI+的知识,如画线、填充等。 ```csharp protected override...

    Java_重写equals()和hashCode()

    总之,理解并正确重写 `equals()` 和 `hashCode()` 方法对于编写高质量的Java代码至关重要,这直接影响到对象比较的逻辑以及使用哈希表的数据结构的效率。通过遵循上述原则和最佳实践,我们可以确保对象的比较行为...

    WPF 窗体样式重写 界面优美

    在WPF中,控件模板(ControlTemplate)用于定义控件的视觉结构,而数据模板(DataTemplate)则用于展示数据对象。 **一、创建自定义样式** 在WPF中,我们可以使用`&lt;Style&gt;`元素在XAML中定义样式。例如,要为`Window...

    水印图片和URL重写

    水印的位置、颜色、透明度等属性都可以根据需求进行调整。另一种方法是利用服务器控件,例如ASP.NET的Image服务器控件,通过CSS样式添加水印效果,这种方式更适用于动态生成的水印。 接下来,我们讨论URL重写。URL...

    ACCP5.0S2教程\使用Java实现面向对象编程(Java OOP)\使用Java实现面向对象编程(Java OOP)上机贯穿案例源码

    5. **构造器**:构造器用于初始化新创建的对象,学习者应了解构造器的语法和作用,以及如何使用构造器参数化对象的创建。 6. **方法重写(Override)与重载(Overload)**:学习者需要掌握如何根据需求重写父类的...

    重写hibernate的session简单增删改查

    在Java的持久化框架中,Hibernate是一个非常流行的ORM(对象关系映射)工具,它使得开发者可以使用面向对象的方式来操作数据库。"重写hibernate的session简单增删改查"是一个针对初学者的实践教程,旨在帮助理解如何...

    两个list比较 取不同的对象

    - 该类重写了`equals`方法,用于比较两个`Student`对象是否相同。这是实现列表比较的关键步骤之一。 3. **比较两个列表的不同项**: - 需要遍历第一个列表,对于每个元素,检查它是否存在于第二个列表中。 - ...

    重写自定义进度条

    在这里,我们可以使用Canvas对象进行绘制,包括进度条的背景、进度指示器等。 2. **绘制逻辑**: - 使用`canvas.drawColor()`设置背景色,可以是纯色或渐变色。 - 使用`RectF`对象定义进度条的边界,然后使用`...

    winform 重写GetHashCode源码

    默认情况下,C#中的`Equals`方法和`GetHashCode`方法是通过引用来判断对象是否相等的,但当我们需要基于对象的属性来判断相等性时,就需要重写这两个方法。`GetHashCode`方法尤其关键,因为它在哈希表(如Dictionary...

    重载、重写、重构.md

    - **重写(Override)**:使用`override`关键字重写父类的方法时,子类的方法会替代父类的方法,即当通过父类引用调用子类对象时,会执行子类中重写的方法。 - **覆盖(New)**:使用`new`关键字隐藏父类的方法时,子类...

    对象存储元数据的索引与查询优化.pptx

    ### 对象存储元数据的索引与查询优化 #### 一、对象存储元数据的索引类型及选择策略 在对象存储系统中,元数据管理是核心组件之一,其主要功能包括存储对象的属性信息(如名称、创建时间、大小等)以及用于快速...

    重写时间控件

    我们需要覆写`OnPaint`方法,使用`Graphics`对象来绘制背景图片,确保在控件大小变化时图片能够适配。在WPF中,我们则会使用XAML来定义样式和模板,包括设置`Background`属性为所需的图片。 2. **资源管理**:背景...

    C# 仿QQ重写窗体外观

    为了模仿QQ的外观,我们需要重写窗体的基本属性和方法,比如背景颜色、边框样式、控件布局等。 1. **重写窗体外观** - 使用`SetStyle`方法可以改变窗体的默认行为,例如禁用系统边框,以便我们可以自定义边框样式...

    python面向对象练习题.pdf

    对象属性是每个对象独有的属性,即每个对象都可以拥有不同的属性值。对象属性用于表示每个对象特有的状态信息。当创建一个新的对象时,该对象将获得自己独立的一套属性值。 #### 三、面向过程编程与面向对象编程的...

    XListCtrl.rar-CListControl重写

    - 性能优化:原生的CListCtrl在处理大量数据时可能会有性能问题,通过重写可以进行优化,比如使用虚拟列表模式(LVS_VIRTUAL)仅在需要时才绘制项。 - 错误修正:可能存在MFC库本身的bug,或者需要兼容特定环境下...

Global site tag (gtag.js) - Google Analytics