前言

虽然使用Object构造函数或者使用对象字面量可以很方便的用来创建一个对象,但这种方式有一个明显的缺点:使用一个接口创建多个对象会产生很多冗余的代码。因此为了解决这个问题,人们开始使用以下几种方式来常见对象。

工厂模式

该模式抽象了创建对象的具体过程,用函数来以特定接口创建对象的细节

    function cPerson(name,sex,age){
    var o = new Object();
    o.name = name;
    o.sex = sex;
    o.age = age;
    o.show = function(){
        console.log(this.name,this.age,this.sex);
    }
    return o;
}
    var p1 = cPerson('谦龙','男','100');
        p1.show();
    var p2 = cPerson('雏田','女','14');
        p2.show();

工厂模式测试

工厂模式测试
工厂方式的问题:使用工厂模式能够创建一个包含所有信息的对象,可以无数次的调用的这个函数。虽然其解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即如何得知一个对象的类型)

构造函数模式

function CPerson(name,sex,age) {//注意这里 构造函数首字母大写
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.show = function () {
        console.log(this.name, this.age, this.sex);
    }
}


var p1 = new CPerson('谦龙','男','100');
    p1.show();
var p2 = new CPerson('雏田','女','14');
    p2.show();

构造函数模式测试
构造函数模式测试

注意构造函数与工厂模式有些不同的地方,如下

  1. 构造函数首字母大写

  2. 没有显式地创建对象

  3. 将属性和方法赋值给了 this对象

  4. 没有return语句

而且以这种方式来调用构造函数会大致经历一下几个步骤

  1. 创建一个新的对象

  2. 将构造函数的作用域赋值给这个对象(因此this就指向了这个对象)

  3. 执行构造函数中的代码(即给新对象添加属性和方法的过程)

  4. 返回对象

注意:构造函数其实和普通的函数没有太大的差别,唯一的不同在于调用方式的不同。以下演示不同的几种调用方式

    // 调用方式1 new 方式
    
    var p1 = new CPerson('谦龙','男','100');
    p1.show();//谦龙 100 男
    
    // 调用方式2 普通函数调用
    
    CPerson('谦龙','男','100');
    window.show()//谦龙 100 男 注意属性和方法会被设置到window对象上
    
    // 调用方式3 在另一个对象的作用域中调用
    var obj = new Object();
        CPerson.call(obj,'谦龙','男','100');
        obj.show(); //谦龙 100 男 在obj的作用域

构造函数的问题:使用构造函数最主要的问题就是每个方法都要在每个实例上重新创建一次,p1与p2的都有show方法,但不是同一个Function的实例,因为function在js中也是一个对象。因此他们共有的show方法并不相等。

原型模式

每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象。而这个对象的用途是 包含可以由特定类型的所有实例 共享的属性和方法。即调用构造函数所创建的那个对象的 原型对象

好处是可以让所有对象的实例共享他的属性的方法。即无需在构造函数中定义实例的信息

 function CPerson(){

    }
    CPerson.prototype.name='谦龙';
    CPerson.prototype.sex='男';
    CPerson.prototype.age=100;
    CPerson.prototype.show=function(){
        console.log(this.name, this.age, this.sex);
    }
    var p1 = new CPerson();
        p1.show(); //谦龙 100 男
    var p2 = new CPerson();
        p2.show();//谦龙 100 男
        console.log(p1.show == p2.show)//true

原型模式测试

原型模式测试

由上图可知p1与p2共享属性和方法

原型模式的问题:

  1. 省略了为构造函数传递初始化参数,导致默认情况下所有实例将得到相同的属性值

  2. 所有属性都会被实例所共享,当属性的类型是引用类型的时候会出一定的问题,实例间对该属性的修改会相互影响
    针对以上所说的第二个问题我们给出实例

        function CPerson(){

        }

        CPerson.prototype.name='谦龙';
        CPerson.prototype.sex='男';
        CPerson.prototype.age=100;
        CPerson.prototype.job=['前端','后端'];
        CPerson.prototype.show=function(){
            console.log(this.name, this.age, this.sex);
        }
        var p1 = new CPerson();
            p1.job.push('测试');
            console.log(p1.job);//["前端", "后端", "测试"]
        var p2 = new CPerson();
            console.log(p2.job);//["前端", "后端", "测试"]
            console.log(p1.job == p2.job);// true

测试

问题测试

由以上可以看出,两个对象p1,p2对job的修改会相互影响,但按照正常思维,实例一般要有自己的全部的属性。

组合使用构造函数和原型模式

该方式利用构造函数定义实例属性、利用原型定义方法和共享的属性,结果每个实例都有一份实例属性的副本,而且共享着方法的引用,可谓是集两家之所长。

 function CPerson(name,sex,age) {//注意这里 构造函数首字母大写
         this.name = name;
         this.sex = sex;
         this.age = age;
         this.job=['前端','后端'];
     }

    CPerson.prototype={
        constructor:CPerson,//注意这里
        show : function () {
            console.log(this.name, this.age, this.sex);
        }
    }

    var p1 = new CPerson('谦龙','男',100);
    var p2 = new CPerson('雏田','女',20);
        p1.job.push('测试');
        console.log(p1.job);//["前端", "后端", "测试"]
        console.log(p2.job);//["前端", "后端"]
        console.log(p1.job == p2.job);//fasle
        console.log(p1.show == p2.show);//true

组合模式测试
组合模式测试

说明:这种组合模式是使用最广泛、认同度最高的一种创建自定义类型的方法。

动态原型模式

动态原型模式将所有的信息都封装在了函数中,而通过构造函数中初始化原型,保持了同时使用构造函数和原型的优点

        function CPerson(name,sex,age) {//注意这里 构造函数首字母大写
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.job=['前端','后端'];
            if(typeof this.show !='function'){ //注意这里 
                console.log('just one');
                CPerson.prototype.show=function(){
                    console.log(this.name, this.age, this.sex);
                }
            }
        }


        var p1 = new CPerson('谦龙','男',100); //just one
        var p2 = new CPerson('雏田','女',20);//没有输出

动态原型模式测试
动态原型模式测试

寄生构造函数模式

该方式的基本思想是创建一个函数,用来封装创建对象的代码,然后再返回新创建的对象。构造函数在不返回值的情况下,默认会返回新对象的实例,而通过return语句可以修改调用构造函数时的返回值。

该方式有一定的应用场景比如,当我们想创建一个具有额外方法的数组而又不能修改Array构造函数的情况下,可以使用这种模式

 function MyOwnArray(){
        var arr=new Array();//创建新对象
            arr.push.apply(arr,arguments);
            arr.show=function(){
                console.log(this.join('|'));
            }
        return arr;
    }

    var arr1 = new MyOwnArray('谦龙','男',100);
        arr1.show();

寄生构造函数模式测试
寄生构造函数模式测试

稳妥构造函数模式

稳妥对象即没有公共属性,方法也不引用this对象,稳妥对象最适合在一些安全的环境中(例如禁止使用this和new)或者防止数据被其他应用程序修改的时候使用。

注意:稳妥构造函数和寄生式构造函数有许多类似的地方,以下是他们的不同之处

  1. 不使用new操作符来调用构造函数

  2. 创建对象的实例方法不使用this对象

  function CPerson(name,sex,age){
        var obj = new Object();
            // private members
        var myOwnName='谦龙';
        obj.showOwnName=function(){
            console.log(myOwnName);//只有通过该方法才能访问myOwnName 私有属性
        }
        obj.show=function(){
            console.log(name,sex,age);
        }
        return obj;
    }

    var p1=CPerson('谦龙','男','100');
        p1.show();
        p1.showOwnName();

稳妥构造函数模式测试
稳妥构造函数模式测试

除了通过调用对应的方法来访问其数据成员,没有别的方法可以访问到原始添加的数据,其提供的这种安全机制适合在例如ADsafe等的环境下使用


前端胖头鱼
3.7k 声望6.2k 粉丝