创建对象的几种方式?(js面向对象设计模式)

1,164 阅读5分钟

一、概述:

1.工厂模式 (缺点:没有解决对象识别的问题(怎样知道一个对象的类型))

2.构造函数模式 (缺点:每个方法都要在每个实例上重新创建一遍)(this)

3.原型模式 (缺点:由原型共享性带来的问题)

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

5.动态原型模式(完美)

if(typeof this._ != 'function'){
     Obj.prototype._ = function(){
        // this._
    };
}

6.寄生构造函数模式 (写法和工厂模式一样,不过还要通过new创建一次对象)

7.稳妥构造函数模式 (遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数)

稳妥对象最合适在一些安全环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序(如Mashup程序)改动使用。

二、详解

1. 使用Object构造函数来创建一个对象;

let person = new Object();
person.name = "wuhua";
person.age = 33;

2. 使用对象字面量创建一个对象;

let person = {
    name: "wuhua",
    age: 33
};

3. 使用工厂模式创建对象;

function Person(name, age){
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name);
    }
    return o;

}

let person = Person("wuhua", 33);

特点:这种模式抽象了创建具体对象的过程。它用函数来封装以特定接口创建对象的细节。

弊端:没有解决对象识别的问题,而且每次生成一个新对象,都要创建新函数sayName,这使得每个对象都有自己的sayName版本,而事实上,所有的对象都共享同一个函数。

4. 使用构造函数模式创建对象;

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name);
    }
}

var person = new Person("wuhua", 33);

PS:构造函数和普通函数的唯一区别就是调用他们的方式不同:任何函数,只要通过new操作符调用就是构造函数,反之就是普通函数。

弊端:每个方法都要在每个实例上重新创建一遍。

使用new操作符创建新实例会经历下面4个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(就是this指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象;

构造函数和工厂模式的不同之处:
没有显示的创建对象;
直接将属性和方法赋给this对象;
没有return语句;

构造函数解决了对象识别的问题,但是就像工厂模式一样,每个方法都会在每个实例中重新创建一遍。我们可以把函数定义转移到构造函数外部来解决这个问题,但是这样就没有封装性可言了。

5. 使用原型模式创建对象;

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

PS:JS中万物皆对象,但分为两大类:普通对象和函数对象。所有的函数对象都有一个prototype属性,普通对象是没有prototype属性的,只有_proto_。

function Person(){}
    Person.prototype.name = "wuhua";
    Person.prototype.age = 33;
    Person.prototype.sayName = function{
    console.log(this.name);

}

let person = new Person();

原型模式虽然解决了构造函数每个方法都会在每个实例中重新创建一遍的问题。但是所有实例在默认情况下都取得了相同的属性值,实例一般都是要有属于自己的全部属性的。

6. 组合使用构造函数模式和原型模式创建对象;

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.sayname = function(){
    console.log(this.name);
};

let person = new Person("wuhua", 33);

这是创建自定义类型的最常见方式,构造函数模式定义实例属性,原型模式定义方法和共享的属性。

7. 使用动态原型模式创建对象;

function Person(name, age){
    this.name = name;
    this.age = age;

    if(typeof this.sayname != "function"){
        Person.prototype.sayName = function(){
            console.log(this.name);
        }
    }
}

var person = new Person("wuhua", 33);

通过在构造函数中初始化原型(仅在必要的情况下),保持了同时使用构造函数和原型的优点。(把所有信息都封装在构造函数中)

8. 使用寄生构造函数模式创建对象;

function Person(name, age){
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name);
    }
    return o;
}

let person = new Person("wuhua", 33);

除了使用new操作符来定义新的对象,以及将其称之为构造函数之外,其他和工厂模式定义一模一样。因为创建时用了new,因此使得实现的过程不一样(但是实现过程不重要),结果一样。看起来更优雅。

寄生构造函数可以在构造函数不适应的情况使用,比如创建具有额外方法的已有类型(如数组,Date类型等),但是又不污染原有的类型。

9. 使用稳妥构造函数模式创建对象;

所谓稳妥对象,指的是没有公共属性,而且其他方法也不引用this对象。稳妥对象最合适在一些安全环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序(如Mashup程序)改动使用。

稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数。

function Person(name, age){
    let o = new Object();
    
    o.sayName = function(){
        console.log(name);
    }

    o.sayAge = function(){
        console.log(age);
    }

    return o;
}

let person = Person("wuhua", 33);

除了调用sayName()方法外,没有别的方式可以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。

10. 使用ES6 class创建对象;

class Person {
  constructor (name, age) {
    this.name = name
    this.age = age
  }

  sayName() {
    console.log(this.name);
  }

  sayAge() {
    console.log(this.age);
  }
}

let person = new Person('wuhua', 33)
person.sayName() // wuhua