• 面向对象与原型


    
    一,创建对象
    var box = new Object();
    box.name = 'lee';
    box.age = 23;
    box.run = function () {
        return this.name + this.age + " run....";
    }
    alert(box.run());
    
    上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box 对象本身。这种是JavaScript 创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。
    
    var box1 = new Object();
    box1.name = 'jack';
    box1.age = 24;
    box1.run = function () {
        return this.name + this.age + " run....";
    }
    alert(box1.run());
    
    为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。
    
    1.工厂模式
    function createObject(name, age)
    {
        var obj = new Object();            //创建对象
        obj.name = name;                    //添加属性
        obj.age = age;
        obj.run = function () {            //添加方法
            return this.name + this.age + " runing...";
        };
    
        return obj;                        //返回对象
    };
    var box2 = createObject('hg', 100);    //创建第一个对象
    var box3 = createObject('wj', 200);    //创建第二个对象
    alert(box2.run());        //打印run方法
    alert(box3.run());
    
    工厂模式:就解决了实例化对象,产生大量重复的问题.但是又引出了一个新的问题,声明出来的对象都是object的类型,无法区分它们.
    
    2.构造函数方法的方式创建对象
    function Box(name, age)        //创建对象
    {
        this.name = name;            //添加属性
        this.age = age;
        this.run = function ()
        {
            return  this.name + this.age + " runing...";
        };
    };
    
    var box1 = new Box('lee', 100);            //实力化    
    var box2 = new Box('wenjing', 200);    
    
    alert(box1.run());
    alert(box2.run());
    
    alert(box1 instanceof Box);        //true
    构造函数方式:(改良后的工厂方法)
    (1)构造函数没有使用new object,但是系统会自动调用var obj=new object().
    (2)this 相当于 obj.
    (3)构造函数方式没有像工厂一样return obj; 后台自动返回对象.
    
    构造函数的方法有一些规范:
    (1)函数名和实例化构造名相同且大写.(非强制,但是有助于区分与普通函数之间的区别).
    (2)通过构造函数创建对象,一定要用new
    (3)解决了对象识别问题box1 instanceof Box 返回ture
    注意:    
    A;this的使用
    this如果在全局,就是代表windows对象,在构造函数内,代表构造函数所声明的对象.构造函数用普通函数调用,一般是无效的,必须使用new运算符.
    B:对象冒充调用:
    var o = new Object();
    Box.call(o, 'Jack', 200)     //对象冒充调用
    alert(o.run());                //正确
    
    C:探讨一下构造函数体内构造方法
    
    function Box(name, age)        //创建对象
    {
        this.name = name;        //添加属性
        this.age = age;
        this.run = function ()
        {
            return  this.name + this.age + " runing...";
        };
    };
    var box1 = new Box('hg', 123);
    var box2 = new Box('wj', 321);
    alert(box1.run == box2.run);        //false
    
    两个对象使用的run函数不相同.解决引用地址不同,可以通过对象外面引用同一个函数,来解决.
    
    function Box(name, age)        //创建对象
    {
        this.name = name;            //添加属性,实例属性
        this.age = age;
        this.run = run;            //实例方法
    };
    function run()
    {
        return  this.name + this.age + " runing...";
    }
    
    var box1 = new Box();
    var box2 = new Box();
    alert(box1.run());
    alert(box1.run == box2.run);        //true
    用全局的run来解决唯一性,但是又带来了新的问题.run暴露在外边.对象调用this代表box,被当作普通函数的时候,this代表window.
    
    
    3.原型
    每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含特定类型的所有实例共享的属性和方法.
    function Box() {};                    //原型
    Box.prototype.name = 'hg';            //原型属性
    Box.prototype.age = 23;
    Box.prototype.run = function ()        //原型方法
    {
        return  this.name + this.age + " runing...";
    }
    var box1 = new Box();
    var box2 = new Box();
    alert(box1.run());
    alert(box1.run == box2.run);        //true
    
    如果是实例方法,不同的实例化,他们的方法地址是不同的地址. 而原型方法,他们的地址是共享的,大家都是一样的.
    
    构造函数方式
    
    
    
    
    
    原型模式方式
    
    __proto__ 是一个指针,指向prototype的原型对象.
    在声明原型的时候,多了2个属性,这2个属性都是创建对象自动生成的, __proto__属性是实例指向原型对象的一个指针.他的作用就是指向构造函数的原型属性constructor.
    PS:IE 浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别。虽然可以输出,但无法获取内部信息。
    alert(box1.__proto__);         //[object Object]
    
    alert(box1.constructor);    //可以获取构造函数本身.
    
    判断一个对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。
    alert(Box.prototype.isPrototypeOf(box1));            //true
    
    原型的执行过程:
    1.先查找构造函数实例里的属性或方法,如果有,立刻返回;
    2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;
    这说明原型是原型,对象是对象.通过原型创建的对象依旧可以添加自己的属性和方法,但是这个属性和方法不影响原型.说明我们可以通过对象访问原型的值,但是不可以改写原型的值.
    Box.prototype.name = 'hg';    //原型属性
    Box.prototype.age = 23;
    
    Box.prototype.run = function ()    //原型方法
    {
        return  this.name + this.age + " runing...";
    }
    
    var box1 = new Box();
    var box2 = new Box();
    
    alert(box2.name);        //hg
    box2.name = 'wj';        //重写name的属性
    alert(box2.name);        //wj
    delete box2.name;        //删除box2的对象name属性
    alert(box2.name);        //hg
    //可以通过prototype来重写原型中的属性.
    Box.prototype.name = 'kkk';    //可以重写原型的属性
    
    可以使用hasOwnProperty()函数来验证:构造函数的实例里是否有属性.
    alert(box.hasOwnProperty('name')); //实例里有返回true,否则返回false
    
    in操作符判断属性是否在对象中,无论属性存在于实例或者原型中都返回true.
    alert('name' in box1);        //true
    alert('name' in box2);        //true
    判断是否属性只存在于原型,而实例中没有.
    function isProperty(object, property) { //判断原型中是否存在属性
    return (!object.hasOwnProperty(property)) && (property in object);
    }
    var box = new Box();
    alert(isProperty(box, 'name')) //true,如果原型有
    
    
    注意:
    var box = new Box();
    alert(box.prototype);        //
    alert(box.__proto__);        //可以,但是IE本身是无法反问的.
    alert(Box.prototype);        //可以
    如果想访问prototype,用Box.prototype可以.
    使用字面量的方式创建原型对象.
    function Box() {}
    Box.prototype = {
        name:'hg',
        age:23,
        run:function () {
            return this.age + this.name + " runing...";
        }
    };
    
    var box = new Box();
    alert(box.run());            //正常打印
    注意:Box.prototype = {};创建了一个新的对象,不再指向Box了.
    可以用constructor:Box进行强制转换.
    
    function Box() {}
    
    Box.prototype = {
        name:'hg',
        age:23,
        run:function () {
            return this.age + this.name + " runing...";
        }
    };
    //这里就切断了之前的原型与对象的链接.不会保存以前的原型中的属性
    Box.prototype = {            
    age:100,
    };
    
    var box = new Box();
    alert(box.run());        //run is undefined
    
    原型的缺点:第一:省略了传参的过程,所有的实例,初始化都是一样的.无法保持独立性.第二个缺点也是它最大的优点就是,所以的都是共享的.
    //原型的缺点
    function Box() {}
    Box.prototype = {
        constructor:Box,
        name:'hg',
        age:100,
        family:['gege', 'jiejie', 'didi'],
        run:function () {
            return this.name + this.family;
        }
    };
    
    var box1 = new Box();
    alert(box1.family);
    box1.family.push('meimei');        //更改了原型
    alert(box1.family);
    
    var box2 = new Box();
    alert(box2.family);                //受box1的操作影响
    
    
    //组合构造函数+原型模式
    function Box(name, age)
    {
        this.name = name;
        this.age = age;
        this.family = ['gg','jj','mm'];
    };
    
    Box.prototype = {
        constructor:Box,
        run:function () {
            return this.name + ' ' + this.family;
        },
    };
    
    var box = new Box();
    alert(box.family);
    box.family.push('dd');    //添加新的数据
    alert(box.family);
    
    var box1 = new Box();
    alert(box1.family);        //没有改变
    
    这样family并没有被共享.
    
    //动态原型模式
    //将原型封装到构造函数里面
    function Box(name, age)
    {
        this.name = name;
        this.age = age;
        this.family = ['gg','jj','mm'];
    //原型初始化开始
        Box.prototype.run = function () {
            return this.name + ' ' + this.family;
        };
    //原型初始化结束
    };
    事实上原型只初始化一次就可以了,所以想办法第一次初始化调用一次就好了.
    function Box(name, age)
    {
        this.name = name;
        this.age = age;
        this.family = ['gg','jj','mm'];
    //这样就只初始化一次,判断run函数是否存在.
        if (typeof this.run != 'function'){
            Box.prototype.run = function () {
                return this.name + ' ' + this.family;
            };    
        }
    
    };
    Var box = new Box(‘hg’, 123);
    Alert(box.run());
    
    
    使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系。重写原型prototype就将之前的原型给替换了.
    
    //寄生构造模式 = 工厂模式 + 构造函数
    function Box(name, age) {
        var obj = new Object();
        obj.name = name;
        obj.age = age;
        obj.run = function () {
            return this.name + this.age + '运行中...';
        };
        return obj;
    }
    
    var box = new Box('hg', 123);
    alert(box.run());
    
    //稳妥构造函数 不允许使用this和new
    在一些安全的环境中,比如禁止使用this 和new,这里的this 是构造函数里不使用this,这里的new 是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。
    function Box(name , age) {
        var obj = new Object();
        obj.run = function () {
        return name + age + '运行中...'; //直接打印参数即可
        };
        return obj;
    }
    var box = Box('Lee', 100); //直接调用函数
    alert(box.run());    
    稳妥构造函数和寄生构造函数很类似.
  • 相关阅读:
    622 CircularQueue C#
    x盒子
    Cygwin、MinG、MSys区别与联系(转)
    Spring集成MyBatis完整示例
    mybatis学习 (五) POJO的映射文件
    mybatis学习(四)——config全局配置文件解析
    json字段为null时输出空字符串
    mybatis学习(一)不使用 XML 构建 SqlSessionFactory
    数据库 ----jdbc连接池的弊端
    Spring @Import注解 —— 导入资源
  • 原文地址:https://www.cnblogs.com/hgonlywj/p/4842613.html
Copyright © 2020-2023  润新知