J2EE社区

苟有恒,何必三更起五更眠;
最无益,只怕一日曝十日寒.
posts - 241, comments - 318, trackbacks - 0, articles - 16

第6章 面向对象程序设计

Posted on 2011-07-20 11:46 xcp 阅读(465) 评论(0)  编辑  收藏 所属分类: JavaScript高级程序设计(第2版)
1、自定义对象
    a. 工厂模式
function Person(name,age){
    
var person = new Object();
    person.name 
= name;
    person.age
=age;
    person.say
=function(){
        alert(person.name
+" "+person.age); 
    }
   return person;
}

var person1 = Person("xcp",23);
var person2 = Person("lxm",24);
person1.say();
person2.say();
//没有解决对象识别的问题(即怎么知道对象的类型)

 

    b. 构造函数模式

function Person(name,age){
    
this.name = name;
    
this.age = age;
    
this.say = function(){
         alert(
this.name+" "+this.age);
    }
}
var person1 = new Person("xcp",23);
var person2 = new Person("lxm",24);
person1.say();
person2.say();
//直接将属性附值于this;并且没有显示的创建和返回对象;必须用new来创建对象
alert(person1 constructor Person);//true
alert(person1 instanceof Person);  //true
    ->创建对象经历以下步骤:
       >创建一个新对象
       >将构造函数的作用赋予新对象(因此this就指向了新对象)
       >执行构造函数里面的代码(添加属性)
       >返回新对象
    ->构造函数的问题
       >使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。例如我们上面的alert(person1.say==person2.say) //false
         创建两个同样功能的Function()是完全没有必要的;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因些,大可以像以下面的代码,通过把函数定义转移到构造函数外部解决这个问题:
        function Person(name,age){
            this.name = name;
            this.age = age;
            this.say    = sayFunction(this.name,this.age);
        }
        function sayFunction(name,age){
            alert(name+" "+age);
        }
        //这样sayFunction就只会创建一次了 //即:alert(person1.say==person2.say) //true;
       但问题也来了,如果有很多个方法,就要创建很多方法,这样无论可读性,适用性都不是很好

    c. 原型模式
        >理解:我们创建的每一个对象,都有一个prototype属性,这个属性是一个对象,那么这个对象的作用是什么呢?所有prototype属性都会自动获得一个constructor(构造函数),这个属性民包含一个指向prototype属性在所函数的指针。Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继承为原型添加其他属性和方法。如图:
    
    
            .Person对象的prototype属性指向Person原理对象
            .Person原理对象的构造方法指向Person对象
            .Person实例调用属性和方法的顺序:先查找Person对象是否具有此属性和方法,如果Person对象具有则返回,如果没有则先查询Person的原型对像。推出:如果对象本身和原型对象同时具有某个函数和方法,以对象本身的属性和方法为准。从这个可以体现出多个对象共享原型所保存的属性和方法,以降低内存开消。但有的时候我们需要用原理对象的属性属性和方法去覆盖原有对象的属性的方法,这时我们采用删除原有对象的属性和方法:delete Person.name(删除原有对象的name属性).可以使用hasOwnProperty()和isPrototypeof();
            
         
        
        >原型与in操作符
            .有两种可能使用in操作:单独使用("name" in person1)和在for..in里面。单独使用in的时候对象能够返回给定属性时返回true(实例和原型)

        >更加简单的原型语法
            function Person(){}
            Person.prototype={
                name:'age',
                age:'23',  
                say:function(){
                        alert(this.name+" "+this.age);
                }
            }
        //这样写下来就相当于重写了对象的prototype对象,那原型对象里面的constructor属性就没有了.但如果我要得到constructor属性怎么办呢?
            Person.prototype={
                constructor:Person,//constructor=this,
                name:'age',
                age:'23',  
                say:function(){
                        alert(this.name+" "+this.age);
                }   
            }
 

        >原型中的动态性           
    .由于原型中查找植的过程是一次性的,因此我们对原型对象所做的操作会立刻反映出来,如
            function Person(){
            }
            Person p = new Person(); 
            Person.prototype.say=function(){
                alert("hi");
            }
            p.say(); //hi;这样就可以立刻体现出来
        
            //再看看下面的例子
            function Person(){}
            Person p = new Person();
            Person.prototype={
                say:function(){
                    alert("hi");
                }
            }
            p.say(); //error;
            //这里为什么就出错了呢,上一个例子不是好好的吗?因为第一个例子,只是在prototype原型对象里面添加了一个属性,我们在调用say的时候他会查找对象本身和原型对象并且是一次查询(所以的代码运行完毕),所以可以正常的到原型对象里面取出来;可是后面一个方法呢,声明对象p他的时候他会找到_proto_指针指向原型对象;而我用Person.prototype也重构这个原型对象,就相当于切断了constructor与是初始原型之间的连接关系,所有p的任何非本身属性都会出错!
 
        >原生态对象模型
            .原型类型不仅体现在自定义对象模型,包括原生的引用类型都是采用的这种模式,所以我们可以修改引用类型提供的方法和属性。
            String.prototype.trim=function(text){...}  //我们将跟String对象添加了一个trim()方法;

        >原型对象的问题
            .原型对象省略了为构造函数传低参数这一细节,结果所以的实例对象取得的实例值都是一样的,所以原型对象最大问题就是由本享本质导成的
            function Person(){}
            Person p = new Person();
            Person.prototype={
                say:function(){
                    alert("hi");
                }
            }
            p.say(); 

            Person p1 = new Person();
            Person p2 = new Person();
            alert(p1.say==p2.say);//true
            //所以接下来我们就是来 解决怎么合理共享,即合理使用prototype
 


        >组合使用构造函数模式和原型模型(最常见的方法)
            .构造函数模式用于定义:实例属性(可以传递多个参数以确保必要性)
             原型模式用于定义:共享的属性和方法(这样即保证了安全,也保证了最大的节约内存)
            
             例:function Person(name,age){
                       this.name=name;
                       this.age = age; 
                       this.friend=["zhangsan","lisi","wangmazi"];
                   }
                   Person.prototype={
                        constructor:Person,
                        say:function(){
                                alert(this.name+" "+this.age);
                        }
                    }
                    Person p1 = new Person("xcp","23");
                    Person p2 = new Person("lxm","25");
                    alert(p1.friends==p2.friends);//false;
                    alert(p1.say==p2.say);//true;
 

            .动态原型模型
                   function Person(name,age){
                       this.name=name;
                       this.age = age; 
                       this.friend=["zhangsan","lisi","wangmazi"];
                        if(typeof this.say !="function"){
                             Person.prototype.say:function(){
                                 alert(this.name+" "+this.age);
                             }
                            //再一次强调这不能写成
                                Person.prototype={...}
                            }
                  }


        >寄生构造函数模型
            .在工厂模式上加上参数
             function Person(name,age){
                var o = new Object();
                o.name=name;
                o.age = age;
                o.say=function(){
                    alert(this.name+" "+this.age);
                }
                return o;
            }








2.继承
    一般所有的OO都支持接口继承和实现继承,而ECMAScript函数没有签明的说话(空函数标志或者说接口的声明方法),所以只有实现继承,而实现继承主要是通过原型对象来实现了。 
    >原型链(重要思想)
        . 简单回顾一下构造函数,原型和实例的关系
            构造函数的prototype属性(指针) -> 原型地址
            原型的construtcotr属性(指针)     ->   构造函数地址
            实例的_proto_属性(内部指针)           ->   原型对象 
        .原型链的思想
            我们将一个原型对象的指针指向另一个构造函数。如此层层递进,就实现了实例和原型的链条
            function SuperType(){
                    this.property = true;
            }
            SuperType.prototype.getSuperValue=function(){
                    return this.property;
            }
            function Subtype(){
                    this.subproperty = false;
            }
            SubType.prototype = new SuperType();  //子类的原型对象指向 生成的超类的子类(所以有一个_proto_指针指向超类的面型对象)
            SubType.prototype.getSubValue=function(){
                    return this.subproperty;
            }
            Subtype instance = new Subtype();
            instance.getSuperValue();
             分析:

                 
            .如果我们调用subType.getSuperValue()经过的步骤有:1)搜索实例本身 2)搜索SubType.prototype  3)搜索SuperType.prototype  4)所有的类都是实现Object,所以如果前面没有找到的话,就还要搜索Object.prototype;所以真正的原型链应该是:
            
            //再到这来这来强调一下,不是原型对象的constructor被重写了,而且_proto_指针被重写了
        
           . 两种方法来确定实现跟原理之间的关系
                //是否是指定类型的实现
                 alert(instance instanceof Object)//true;
                alert(instance instanceof SuperType);//true;
                alert(instance instanceof SubType);//true;
                alert(instance instanceof Data);  //false;
                
                //指定类型的原型对象是否派生了此实例
                alert(Object.prototype.isPrototype(instance));//true;
                alert(SuperType.prototype.isPrototype(instance));//true;
                alert(SubType.prototype.isProtoType(instance));//true
        
            .重写超类方法
                在SubType.prototype = new SuperType();  后面添加如下:
                //重写超类方法
                SubType.prototype.getSuperValue=function(){
                    return false;
                }

                //不能写成这样
                SubType.prototype=new Supertype();
                SubType.prototype={
                     getSuperValue=function(){
                        return false;
                    }
                } //当一使用这一句话时,上一句话就立刻实效了,为什么呢?前面已经说过一这样就切断了SubType与SuperType.prototype的链条
 
            .原型链的问题
                最主要的问题就是包含引用类型类型的值,换一句就是访问安全性问题;怎么说来着?前面介绍包含引用类型值的原型属性民会被所有实例共享;而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。但我们通过原型来实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性和原型对象里面所有的属性和方法顺利成章地变成了现在的原型属性了,从而可以将属性工享这就破坏了原有的安全性了。


        >借用构造函数(也叫伪造对象和经典继承->常用方式)
            .无参数版
                function SuperType(){
                    colors:["red","blue","green"];
                }
                function SubType(){
                    //实现继承SuperType,为什么呢,因为在子类构造函数调用父类构造函数里面的call方法
                    SuperType.call(this);
                }
                var instance1 = new SubType();
                instance1.colors.push("block");
                alert(instance1.colors); //"red","blue","green","block";
            
                var instance2 = new SubType();
                alert(instance2.colors);   //"red","blue","green"                                   
            
            .有参数版
                function  SuperType(name){
                        this.name = name;
                }
                function SubType(){
                    //继承了SuperType,同时传递了参数    
                    SuperType.call(this,"xcp"); 
                    //实例属性
                    this.age = 23;     
                }
                var instance = new SubType();
                alert(instance.name); //xcp
                alert(instance.age); //23
              
            .借用构造函数的问题
                如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题-方法都在构造函数中定义,这样些函数复用就无从谈起了。

        >组合继承(也称伪经典继承),指的是将原型链和借用构造函数的技术结合在一起来,从而发挥者这宅的一种继承模式
            .实例(精典实例)
                function SuperType(name){
                    this.name  = name;
                    this.colors = ["red","blue","green"];
                }
                SuperType.prototype.sayName=function(){
                    alret(this.name);
                }
                function SubType(name,age){
                    //继承属性
                    SuperType.call(this,name);
                        
                    //添加新属性
                    this.age = age;
                }
                
                //继承原型里面的属性和方法
                SubType.prototype = new SuperType();
                //添加新方法
                SubType.prototype.sayAge = function(){
                        alert(this.age);
                }

                var instance1 = new SubType("xcp",23);
                instance1.colors.push("black");
                alert(instance1.colors); //red,blue,green,black
                instance1.sayName();//xcp;
                instance1.sayAge();//23;
        
                var instance2 = new SubType("lxm",25);
                alert(instance2.colors); //red,blue,green
                instance2.sayName();//lxm
                instance2.sayAge();  //25

                //组合继承就避免了前面的原型链(父类所有的属性和方法均作用子类的原型对象的属性和方法,所以就存在数据安全问题了)和借用构造函数的缺陷(无共享属性和方法机制);成为Javascript中最常用的继承模式。而且,instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。
               
         >当然还包括:原型式继承、寄生式继承等






3.总结
    >创建javascript对象的方式
        .方法:
            构造方法式<接收参数传值实例属性>+原型式<抽取公共共享的方式>
        .实例:
            function Person(name,age){
                this.name=name;
                this.age = age; 
                this.friend=["zhangsan","lisi","wangmazi"];
            }
            Person.prototype={  //使用最简对象完成
                 constructor:Person,
                 say:function(){
                     alert(this.name+" "+this.age);
                 }
            }
            Person p1 = new Person("xcp","23");
            Person p2 = new Person("lxm","25");
            alert(p1.friends==p2.friends);//false;
            alert(p1.say==p2.say);//true;
    
    >继承超类的方法
        .方法
            借用构造函数式<复用超类的实例变量,表示不同实例得到不变量不同>+原型链<复用超类的原型对象,意思就是子类的_proto_指针指同超类的原型对象>
        .实例
            function SuperType(name){
                    this.name  = name;
                    this.colors = ["red","blue","green"];
                }
                SuperType.prototype.sayName=function(){
                    alret(this.name);
                }
                function SubType(name,age){
                    //继承属性
                    SuperType.call(this,name);
                        
                    //添加新属性
                    this.age = age;
                }
                
                //继承原型里面的属性和方法
                SubType.prototype = new SuperType();
                //添加新方法
                SubType.prototype.sayAge = function(){
                        alert(this.age);
                }

                var instance1 = new SubType("xcp",23);
                instance1.colors.push("black");
                alert(instance1.colors); //red,blue,green,black
                instance1.sayName();//xcp;
                instance1.sayAge();//23;
        
                var instance2 = new SubType("lxm",25);
                alert(instance2.colors); //red,blue,green
                instance2.sayName();//lxm
                instance2.sayAge();  //25



名称: ♪4C.ESL | .↗Evon
口号: 遇到新问题♪先要寻找一个方案乄而不是创造一个方案こ
mail: 联系我