JavaScript:prototype

来源:互联网 发布:手写字体软件下载 编辑:程序博客网 时间:2024/05/01 20:59

JavaScriptprototype属性使用说明

prototype 是在 IE 4 及其以后版本引入的一个针对于某一类的对象的方法,而且特殊的地方便在于:它是一个给类的对象添加方法的方法!这一点可能听起来会有点乱,别急,下面我便通过实例对这一特殊的方法作已下讲解:

   首先,我们要先了解一下类的概念,JavaScript 本身是一种面向对象的语言,它所涉及的元素根据其属性的不同都依附于某一个特定的类。我们所常见的类包括:数组变量(Array)、逻辑变量 (Boolean)、日期变量(Date)、结构变量(Function)、数值变量(Number)、对象变量(Object)、字符串变量 (String) 等,而相关的类的方法,也是程序员经常用到的(在这里要区分一下类的注意和属性发方法),例如数组的push方法、日期的get系列方法、字符串的 split方法等等,

  但是在实际的编程过程中不知道有没有感觉到现有方法的不足?prototype 方法应运而生!下面,将通过实例由浅入深讲解 prototype 的具体使用方法:


1
、最简单的例子,了解 prototype
(1) Number.add(num)
:作用,数字相加
实现方法:Number.prototype.add = function(num){return(this+num);}
试验:alert((3).add(15)) -> 显示
18


(2) Boolean.rev():
作用,布尔变量取反

实现方法:Boolean.prototype.rev = function(){return(!this);}
试验:alert((true).rev()) -> 显示
false

是不是很简单?这一节仅仅是告诉读者又这么一种方法,这种方法是这样运用的。



2
、已有方法的实现和增强,初识 prototype
(1) Array.push(new_element)
  作用:在数组末尾加入一个新的元素
  实现方法:
  Array.prototype.push = function(new_element){
        this[this.length]=new_element;
        return this.length;
    }
  让我们进一步来增强他,让他可以一次增加多个元素!

  实现方法:
  Array.prototype.pushPro = function() {
        var currentLength = this.length;
        for (var i = 0; i < arguments.length; i++) {
            this[currentLength + i] = arguments[i];
        }
        return this.length;
    }
  应该不难看懂吧?以此类推,你可以考虑一下如何通过增强 Array.pop 来实现删除任意位置,任意多个元素(具体代码就不再细说了)


(2) String.length
  作用:这实际上是 String 类的一个属性,但是由于 JavaScript 将全角、半角均视为是一个字符,在一些实际运用中可能会造成一定的问题,现在我们通过 prototype 来弥补这部不足。
  实现方法:
  String.prototype.cnLength = function(){
        var arr=this.match(/[^/x00-/xff]/ig);
        return this.length+(arr==null?0:arr.length);
    }
  试验:alert("EaseWe空间Spaces".cnLength()) -> 显示
16
  这里用到了一些正则表达式的方法和全角字符的编码原理,由于属于另两个比较大的类别,本文不加说明,请参考相关材料。



3
、新功能的实现,深入 prototype:在实际编程中所用到的肯定不只是已有方法的增强,更多的实行的功能的要求,下面我就举两个用 prototype 解决实际问题的例子:
(1) String.left()
  问题:用过 vb 的应该都知道left函数,从字符串左边取 n 个字符,但是不足是将全角、半角均视为是一个字符,造成在中英文混排的版面中不能截取等长的字符串
  作用:从字符串左边截取 n 个字符,并支持全角半角字符的区分
  实现方法:
  String.prototype.left = function(num,mode){
        if(!//d+/.test(num))return(this);
        var str = this.substr(0,num);
        if(!mode) return str;
        var n = str.Tlength() - str.length;
        num = num - parseInt(n/2);
        return this.substr(0,num);
    }
  试验:

     alert("EaseWe
空间Spaces".left(8)) -> 显示 EaseWe空间
     alert("EaseWe
空间Spaces".left(8,true)) -> 显示 EaseWe
  本方法用到了上面所提到的String.Tlength()方法,自定义方法之间也能组合出一些不错的新方法呀!

(2) Date.DayDiff()
  作用:计算出两个日期型变量的间隔时间(年、月、日、周)
  实现方法:
  Date.prototype.DayDiff = function(cDate,mode){
        try{
            cDate.getYear();
        }catch(e){
            return(0);
        }
        var base =60*60*24*1000;
        var result = Math.abs(this - cDate);
        switch(mode){
            case "y":
                result/=base*365;
                break;
            case "m":
                result/=base*365/12;
                break;
            case "w":
                result/=base*7;
                break;
            default:
                result/=base;
                break;
        }
        return(Math.floor(result));
    }
  试验:alert((new Date()).DayDiff((new Date(2002,0,1)))) -> 显示
329
     alert((new Date()).DayDiff((new Date(2002,0,1)),"m")) ->
显示
10
  当然,也可以进一步扩充,得出响应的小时、分钟,甚至是秒。


(3) Number.fact()
  作用:某一数字的阶乘
  实现方法:
  Number.prototype.fact=function(){
        var num = Math.floor(this);
        if(num<0)return NaN;
        if(num==0 || num==1)
            return 1;
        else
            return (num*(num-1).fact());
    }
  试验:alert((4).fact()) -> 显示
24
  这个方法主要是说明了递归的方法在 prototype 方法中也是可行的!

javascript prototype 继承

关键字: javascript prototype extend

js中基于prototype实现继承的基本代码如下所示:

function(SubClass, SuperClass){
    function F(){}
    // 
    F.prototype = SuperClass.prototype;
    // 实现继承的关键,构造 prototype chain
    SubClass.prototype = new F(); // 1
    // 重置子类prototype对象的constructor属性为子类本身。
    SubClass.prototype.constructor = SubClass;
    // 设置子类的superclass属性值为父类的prototype
    SubClass.superclass = SuperClass.prototype;  // 2
    // 使得在子类中可以通过baseconstructor来访问父类的构造函数。
    SubClass.baseconstructor = SuperClass;
}

 上述代码中有两个值得关注的地方:

1)语句1处使用了new F()的方式,这里为什么需要专门new一个F对象出来呢?首先我们来看下面的这段代码的结果:

function SP(){this.cls = "super class";}
SP.prototype.print(alert(this.cls););
 
function SB(){}
SB.prototype = SP.prototype
SB.prototype.print(alert("changed by subclass"));
 
new SP().print();  // output: changed by subclass

 从结果可知SB prototype对象的改变同时影响到了SP,也即此时SBSP是共享同一个prototype对象。这是不符合继承的本意的。这里如果将

SB.prototype = SP.prototype

更改为:

SB.prototype = new SP()

则可以避免此问题。这也是代码1处使用了new F() 而不是F.prototype的原因。

这里还有一个疑问就是F在这里具体作用是什么?为什么需要添加F,而不是直接使用new SuperClass()

先看下面这段代码:

function SP(){this.cls = "super class";}
SP.prototype.print(alert(this.cls););
 
function SB(){}
SB.prototype = new SP();
 
new SB().print();  // super class
 
function F(){}
F.prototype = SP.prototype;
SB.prototype = new F();
 
new SB().print();  // undefined

 代码中两次执行print函数打印出的结果分别是“super class” undefined。也即第二次执行此函数时SB对象中不存在cls属性,这正是添加一个空的F函数的作用。它可以避免在子类SB中获得得到在父类SP函 数体中定义的属性(this.xxx)

2)语句2SubClass.superclassSuperClass.prototype,而不是SuperClass本身。关于这一点的说明,引用 http://bbs.51js.com/viewthread.php?tid=72688&page=1&extra=#pid556697 上某位网友的观点:

 

原型链继承 subclass就是通过prototype的技巧性处理来继承superclass 这本身是一个逐级向上的递归过程 所以指定superclass指定到superclassprototype比较合理,而且以后调用也方便 只需要subclass.superclass.method.call 如果像你那样指定的话就需要subclass.superclass.prototype.method.call 反而更复杂了
subclass.superclass.call(this)
的方式没有subclass.superclass.constructor.call(this)的方式直观
而且构造函数constructor我感觉也是显示调用比较好,风格比较一致,而且少点潜规则也更容易看懂吧,没必要学java的那种方式,而且如果superclass没有构造函数的话 按照ext的方式会逐级向更上层的父类调用,直到最顶端。

 Ext中的继承也是基于prototype chain的方式来实现的,在 http://www.javaeye.com/topic/195409 有对其继承方法实现的详细解析。但是,其中有一处代码一直未弄懂其作用:

 if(spp.constructor == Object.prototype.constructor){
                     spp.constructor=sp;
   } 

 这一代码中,if条件成立时有两种可能:(1sp本身就是Object;(2)函数对象在创建时,其prototype对象的 constructor属性值总是指向函数对象本身。因此另一种可能就是sp显示改变了其prototype对象,而新对象的constructor属性 值指向的是Object。第1种情况处理没有任何意义,因为此时spp.constructor一定等于sp;那么只可能是第二种情况。一个令人费解的问题是"为什么要到有子类继承时才重置父类protoype对象constructor的值,而不是在显示改变其prototype对象时就设置"。为什么 这里需要重置constructor的值呢?结合前后代码来看:

spp = sp.prototype;
sb.superclass=spp;
 
 if(spp.constructor == Object.prototype.constructor){
                     spp.constructor=sp;
  }  

 同上文中关于语句2处设置子类superclass一样,此处sbsuperclass属性设置为了spp,其父类spprototype对象。此时,若想访问到父类本身,就只能通过sppconstructor属性。因此,才需要对constructor属性进行判断,将其重置为父类 sp。也即,加入此语句的原因是为了能够保证在子类中正确调用到父类的构造函数 ,而不是Object

例:<script type="text/javascript">
//
原型继承

function classA(){
 this.MethodA = function(){
  alert('classA.MethodA()');
 }
}

function classB(){
 this.MethodB = function(){
  alert('classB.MethodB()');
 }
}

classB.prototype = new classA();

var a = new classB();
a.MethodA();
a.MethodB();
</script>

原创粉丝点击