创建对象的常用四种模式和优缺点

一、工厂模式

        function createCar(color,type){
            var car = new Object();
            car.color = color;
            car.type = type;
            car.sayColor = function(){
                alert(this.color);
            };
            return car;
        }
        var car1 = createCar('red','CDV');
        var car2 = createCar('blue','SUV');
        car1.sayColor();  //red
        car2.sayColor();  //blue
        console.log(car1);
        console.log(car2);

  运行结果:

  1.  特点:用函数来封装创建对象的细节,内部最终返回一个对象。  

  2.  优点:解决了创建多个相似对象的问题

  3.  缺点:不能确定对象的类型(即无法用 instanceof 去判断)

二、构造函数模式

        function Car(color,type){
            this.color = color;
            this.type = type;
            this.sayColor = function(){
                alert(this.color);
            };
        }
        var car1 = new Car('red','CDV');
        var car2 = new Car('blue','SUV');
        car1.sayColor();  //red
        car2.sayColor();  //blue
        console.log(car1);
        console.log(car2);
        console.log(car1 instanceof Car);

  1、特点:

    • 没有显式创建对象
    • 属性和方法赋给了this
    • 没有 return 语句

  2、调用过程:

    • 使用new操作符创建一个新对象
    • 将构造函数的作用域赋给新对象(this就指向了新对象)
    • 执行构造函数里的代码
    • 返回新对象

  3、优点:可将实例标识为一种特定类型(比如car1的类型就是Car,可用instanceof判断)

  4、缺点:每个方法都要在每个实例上重新创建一遍。可以将其写在外部共享,但又失去了封装性。如下:

        function Car(color,type){
            this.color = color;
            this.type = type;
        }

        function sayColor(){
            alert(this.color);
        }
        var car1 = new Car('red','CDV');
        var car2 = new Car('blue','SUV');
        sayColor.call(car1);  //red,这里通过call来改变作用域
        sayColor.call(car2);  //blue,这里通过call来改变作用域
        console.log(car1);
        console.log(car2);
        console.log(car1 instanceof Car);

  

三、原型模式

        function Car(){
        }
        Car.prototype.color = 'red';
        Car.prototype.type = 'SUV';
        Car.prototype.sayColor = function(){
            alert(this.color);
        }
        var car1 = new Car();
        var car2 = new Car();
        
        car1.sayColor(); //red 来自原型
        car2.sayColor(); //red 来自原型
        car1.color = 'blue'; //屏蔽了原型的color,但没有覆盖,原型的color还在
        alert(car1.color);  //blue 来自实例
        alert(car2.color);  //red  来自原型

  1、原型、构造函数与实例的关系:每个构造函数都有一个原型对象(prototype属性),原型对象包含一个指向构造函数的指针(constructor),实例包含一个指向原型对象的指针(__proto__)。实例与构造函数没有直接关系。

  2、访问一个对象属性的过程:在上面代码中,比如car2.color ,先问car2实例上有color属性吗?没有,则查找原型,car2原型上有color属性吗?有,则返回。再比如car1.color ,先问car1上有color属性吗?有,则返回。就不需要查找原型了。

  3、判断属性是否存在于实例中:hasOwnProperty() , true——实例

  4、优点:属性和方法可以共享,并且可以在实例上重新定义属性,而不改变原型。

  5、缺点:由于共享,对于引用类型,在实例上进行修改,也会改变原型的值。如下:

  

        function Person(){
        }
        Person.prototype = {
            constructor:Person,  //防止一些操作切断构造函数与原型的关系
            name:'Jemma',
            friends:['Lucy','Jack']
        }
        var person1 = new Person();
        var person2 = new Person();
        console.log(person1.name); //Jemma
        console.log(person2.name); //Jemma
        //改变基本类型的属性
        person1.name = 'King';
        console.log(person1.name); //King
        console.log(person2.name); //Jemma
        //改变引用类型
        person1.friends.push('Marry');
        console.log(person1.friends); //["Lucy", "Jack", "Marry"]
        console.log(person2.friends); //["Lucy", "Jack", "Marry"]

  

四、构造函数+原型模式(一般使用这个)

        function Person(name,age){
            this.name = name;
            this.age = age;
        }
        Person.prototype = {
            constructor:Person,  //防止一些操作切断构造函数与原型的关系
            sayName:function(){
                alert(this.name);
            }
        }
        var person1 = new Person('Jemma',19);
        var person2 = new Person('King',21);
        console.log(person1.name); //Jemma
        console.log(person2.name); //King
        person1.sayName(); //Jemma
        person2.sayName(); //King

  特点:所有实例共享的属性和方法原型中定义;自己独有的在构造函数定义。

  优点:既可以共享,又有特定类型。


 

  其实关于这些细节可以讲很多东西,我这里只是简单总结一下。

posted @ 2019-07-13 15:45  Gianna_w  阅读(912)  评论(0编辑  收藏  举报