zoukankan      html  css  js  c++  java
  • 面向对象与原型

      ECMAscript有两种开发模式,1.函数式(过程化),2.面向对象(OOP)。面向的对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象,但是,ECMAscript没有类的概念,因此他的对象也与基于类的语言中的对象有所不同。

      

    var box =new Object();                //创建一个对象
    box.id="杜伟";                        //给对象添加一个属性
    box.ag=33;
    box.show=function(){                //为对象添加一个方法
        alert("你好,我的名字叫"+this.id+"我今年"+this.ag+"岁!");
    }
    //使用对象初始化器来创建对象
    // var box={
    //     id:"杜伟",
    //     agg:33,
    //     show:function(){
    //         alert(this.id);
    //     }
    // };
    
    // box.show();
    
    
    alert(box.show());    //输出方法
    alert(box.id);        //输出属性

    上面创建了一个对象,并且创建属性和方法,在show方法里的this,就是代表box对象本身。这种事javascript创建对象最基本的方法没,但是有个缺点,想创建一个类似的对象,就会产生大量的代码。

    工厂模式

    function createobj(id,agg){
        var obj=new Object();     //创建对象
        obj.id=id;          //添加属性
        obj.agg=agg;
        obj.show=function(){    //添加方法
            return this.agg;
        }
        return obj;        //返回对象
    }
    
    var box=createobj("杜伟",33);    //创建第一个对象
    var box1=createobj("王玥",34);    //创建第二个对象 alert(box.show());
    alert(box1.show()); alert(box.id);

    工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例

     构造函数创建 

    function Box(user,age){           //创建一个构造方法,所有构造方法的对象就是Object
        this.user=user;                                            //添加一个属性
        this.age=age;    
        this.run=function (){                                    //添加一个方法
            return this.user+this.age+"运行中。。。。";
        };
    }
    
    var box=new Box("杜伟",33);                //实例化box
    var box1=new Box("王玥",35);
    alert(box.run());                        //输出box
    alert(box1.run());

    1.构造函数没有工厂模式中的new Object,但它会在后台自动var obj=new Object();

    2.this就相当于obj

    3.构造函数不需要返回对象引用,它是后台自动返回的

    构造方法的规范

    1.构造函数也是函数,但函数名第一个字母大写

    2.必须new构造函数名(),new Box(),而这个Box第一个字母也是大写的

    3.必须使用new 运算符

    function Box(user,age){                                        //创建一个构造方法
        this.user=user;                                            //添加一个属性
        this.age=age;    
        this.run=function (){                                    //添加一个方法
            return this.user+this.age+"运行中。。。。";
        };
    }
    
    function Des(user,age){                                        //创建一个构造方法
        this.user=user;                                            //添加一个属性
        this.age=age;    
        this.run=function (){                                    //添加一个方法
            return this.user+this.age+"运行中。。。。";
        };
    }
    var box=new Box("杜伟",33);                //实例化box
    var box1=new Box("王玥",35);
    var box2=new Des("中国",6000);
    alert(box.run());                        //输出box
    alert(box1.run());
    alert(box2.run());
    alert(box instanceof Object);        //true
    function Box(user,age){                                        //创建一个构造方法
        this.user=user;                                            //添加一个属性
        this.age=age;    
        this.run=function (){                                    //添加一个方法
            return this.user+this.age+"运行中。。。。";
        };
    }
    
    
    //对象冒充
    var o=new Object();
    Box.call(o,"wangyue",23);            //对象冒充
    alert(o.run());                    //o对象就有了Box的对象了
    alert(o.run);            //打印出run方法的引用地址
    function Box(user,age){                                        //创建一个构造方法
        this.user=user;                                            //添加一个属性
        this.age=age;    
        this.run=run;        
        
    }
    
    function run(){     //把构造函数内部的方法,通过全局来实现引用地址一至
        return this.user+this.age+'运行中。。。。'
    }
    
    var box=new Box("lee",100);
    var box1=new Box("lee",100);
    alert(box.run==box1.run);//返回true  ,如果是在构造方法中直接写内部函数就不相等,而内部方法每次实例化地址都不一样

    三、原型 

      我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法,逻辑上可以这么理解,prototype通过调用构造函数创建的那个对象的原型对象,使用原型的好处可以让所有对象实例共享它所有包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

    //构造函数
    function Box(user,age){
        this.name=user;
        this.id=age;
        this.run=function(){
            return this.name+this.id+"运行中。。 。";
        };
    
    };
    var box=new Box("duw",33);
    alert(box.run());
    
    //原型
    function Box(){}                    //构造函数体内什么的都没有
    Box.prototype.user="Lee";                //原型属性
    Box.prototype.age=33;
    Box.prototype.run=function(){            //原型方法
     return    this.user+this.age+"运行中。。。";
    };
    var fei=new Box();
    alert(fei.run());
    alert(box.run==fei.run);     //地址相等
    //如果是实例方法,不同的方法的实例化,他们的方法的地址是不一样的,是唯一的
    //如果是原形方法,那么她们的地址是共享的,大家都一样

    在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的,__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor,通过这两个属性,就可以访问到原型里的属性和方法了。

    ps:IE浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别,虽然可以输出,反无法获取内部信息

    //原型
    function Box(){}                    //构造函数体内什么的都没有
    Box.prototype.user="Lee";                //原型属性
    Box.prototype.age=33;
    Box.prototype.run=function(){            //原型方法
     return    this.user+this.age+"运行中。。。";
    };
    var fei=new Box();
    alert(fei.prototype);            //访问不到  这是一个对象  返回undefined
    alert(fei.__proto__);            //可以访问  这是prototype的指针 返回Object
    alert(fei.constructor);            //构造属性,可以获取构造函数本身,作用是被原型
                                    //指针定位,然后得到构造函数本身,其实就是实例对象
                                    //对应的原型对象的作用
    var lei=new Box();
    //判断一个对象实例(对象引用)是不是指向了原型对象,基本上只要实例化了,它自动指向的
    alert(Box.prototype.isPrototypeOf(lei));   //true
    function Box(){
        //如果这里这么写  打印出来的就是sdfsdf
        this.user="sdfsdf";
    };
    Box.prototype.user="Lee";          //原型属性
    Box.prototype.age=33;
    
    var box1=new Box();                //实例化
    box1.user="duwei";                //添加实例属性    实例属性并没有重写原型属性
    alert(box1.user);            // 打印出duwei  ,就近原则

    原型模式的执行流程:

      1.先查找构造函数实例里的属性或方法,如果有,立刻返回

      2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回:

    function Box(){
        //如果这里这么写  打印出来的就是sdfsdf
        this.user="sdfsdf";
    };
    Box.prototype.user="Lee";          //原型属性
    Box.prototype.age=33;
    
    var box1=new Box();                //实例化
    box1.user="duwei";                //添加实例属性    实例属性并没有重写原型属性
    alert(box1.user);            // 打印出duwei  ,就近原则
    delete box1.user;            //删除实例属性   删除原型属性  delete Box.prototype.user
    alert(box1.user);          //打印Lee

    原型的缺点

      原型模式创建对象也有自己的缺点,它省略构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是他最大的有点,那就是共享。

      原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本只得属性也还可以,但如果属性包含引用类型,就存在一定的问题:

      

    // 原型缺点
    function Box(){};
    Box.prototype={
        user:"杜伟",
        age:33,
        family:['父亲','母亲','子女'],        //添加一个数组属性
        run:function(){
            return this.user+this.age+this.family;
        }
    
    };
    
    var box1=new Box();
    //box1.family[0]="姐妹";   如果我修改数组中的属性,box1  box2都会改变
    alert(box1.run());
    var box2=new Box();
    alert(box2.run());      //因为与原型是共享的,实例化第二个的时候会和第一个一样

    为了解决构造传参和共享问题,可以组合构造函数+原型模式:

    // 构造函数+原型模式
    function Box(user,age){                                //不共享的使用构造函数
        this.user=user;
        this.age=age;
        this.family=['父亲','姐妹','子女'];
    };
    Box.prototype={                                        //共享的使用原型
        constructor:Box,
        run:function (){
            return this.user+this.age+this.family
        }
    };
    
    var box1=new Box('杜伟',33);        //实例1
    alert(box1.run());   
    var box2=new Box('王玥',34);        //实例2
    alert(box2.run());

    PS:这种混合模式很好的解决了传参和引用共享的大难题,是创建对象比较好的方法。

    原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型部分让人感受又很怪异,最好就是把构造函数和原型封装到一起,为了解决这个问题,我们可以使用动态原型模式。

    //动态原型模式
    function Box(user,age){                            //将所有信息封装到函数体内
        this.user=user;
        this.age=age;
        if (typeof this.run!='function')      //仅在第一次调用的初始化   判断类型 run 是不是函数
            Box.prototype.fun=function(){
                return this.user+this.age
            };
    
    };
    var box1=new Box('杜伟',33);
    alert(box1.fun());
    alert(box1.run());        //错误  不存在

    当第一次调用构造函数时,run()方法发现不存在,然后初始化原型,当第二次调用,就不会初始化,。并且第二次创建新对象,原型也不会再初始化,这样即得到了封装,又实现了原型方法共享,并且属性都保持独立。

    PS:使用动态原型模式,要注意一点,不可以在使用字面量的方式重写原型,因为会切断实例和新原型之间的联系

    以上讲解了各种方式对象创建的方法,如果这几种方式都不能满足需求,可以使用一开始那种模式:寄生构造函数:

      寄生构造函数,其实就是工厂模式+构造函数模式,这种模式比较通用,单不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。

      在什么情况下使用寄生构造函数比较合适呢?架设要创建一个具有额外方法的引用类型,由于之前说明不建议直接String.addstring,可以通过寄生构造函数的方式添加。

       

    // 寄生构造函数
        function Box(user,age){
            var obj=new Object();
            obj.user=user;
            obj.family=age;
            obj.run=function(){
                return this.user+this.family+"运行中!";
            };
            return obj;
        };
    
    var box1=new Box('杜伟',33);
    alert(box1.run());

    稳妥构造函数:

      在一些安全的环境中,比如禁止使用this和new,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数不使用new,这种创建方式叫做稳妥构造函数。

    // 稳妥构造函数
        function Box(user,age){
            var obj=new Object();
            obj.user=user;
            obj.age=age;
            obj.run=function(){
                return user+age+"运行中!";
            };
            return obj;
        };
    
    var box1=Box('杜伟',33);
    alert(box1.run());
  • 相关阅读:
    程序打包(通过VS2015插件 InstallShield2015 Limited Edition 方式)
    QSS单独设置四个边角
    Qt 设置窗口圆角
    Qt实现鼠标拖动窗口
    关于split切割的几种情况
    java开发过程中,报错Dangling meta character '*' near index 0,解决办法
    关于ArrayList的contains方法
    WPS文档按回车键不能换行怎么解决
    正则表达式截取数字
    如何用hibernate中查询出一个表数量总数
  • 原文地址:https://www.cnblogs.com/xiaowie/p/10083544.html
Copyright © 2011-2022 走看看