【JS】JavaScript创建对象 - 工厂模式 - 构造函数模式 - 原型模式 - 原型链 - 组合模式

文章目录

  • 0. 创建单个普通对象
  • 1. 工厂模式
  • 2. 构造函数模式
  • 3. 原型模式
  • 4. 组合模式
  • 5. 动态原型模式
  • 6. 寄生构造函数模式
  • 参考

0. 创建单个普通对象

简单的创建一个对象,可以使用Object构造函数,也可以直接使用字面量的方式。

let o = new Object();
let obj = {};

如果我们需要创建一些类似的对象,这样就会产生很多重复的代码。
过去的JavaScript中也没有类的概念,为此聪明的JavaScript程序员使用了很多模式来创建对象

1. 工厂模式

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

这样可以创建多个功能相似的对象

let person1 = createPerson("yk", 18, "software engineer");
let person2 = createPerson("ykk", 12, "student");

但是这样做有些问题,就是创建出来的对象到底还是Object的实例,无法判断它到底是什么具体类的实例,所以采用构造函数模式来解决这个问题

优点:解决了创建多个相似对象时,代码的复用问题
缺点:使用工厂模式创建的对象,没有解决对象识别的问题

2. 构造函数模式

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

一样可以创建很多相似的对象出来

let person1 = new Person("yk", 18, "software engineer");
let person2 = new Person("ykk", 12, "student");

这里创建出来的属性和方法,都在实例对象中。也就是每次创建一个对象,都会将属性和方法在对象中重新创建一遍,对于方法来说,这样是有点重复的。因为很多方法功能是一样的,没有必要为每一个实例对象都创建一个方法。
【JS】JavaScript创建对象 - 工厂模式 - 构造函数模式 - 原型模式 - 原型链 - 组合模式_第1张图片

console.log(person1.constructor === Person); // true
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true

优点:解决了工厂模式中对象类型无法识别的问题,并且创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型
缺点:在使用构造函数创建对象时,每个方法都会在实例对象中重新创建一遍。

3. 原型模式

将属性和方法都放在构造函数的原型上,就可以让实例对象共享属性和方法,节约内存

function Person() {}
Person.prototype.name = "yk";
Person.prototype.age = 18;
Person.prototype.job = "student";
Person.prototype.sayName = function () {
  console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
console.log(person1.sayName === person2.sayName); // true

【JS】JavaScript创建对象 - 工厂模式 - 构造函数模式 - 原型模式 - 原型链 - 组合模式_第2张图片
由于原型链的存在,通过实例对象可以访问到原型对象上的属性和方法

console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);
console.log(Person.prototype.__proto__ === Object.prototype);
console.log(Person.prototype.__proto__.constructor === Object);
console.log(Person.prototype.__proto__.__proto__ === null);

【JS】JavaScript创建对象 - 工厂模式 - 构造函数模式 - 原型模式 - 原型链 - 组合模式_第3张图片

优点:解决了构造函数模式中多次创建相同函数对象的问题,所有的实例可以共享同一组属性和函数。

缺点:省略了构造函数模式传递初始化参数的过程,所有的实例在默认情况下都会取得默认的属性值,会在一定程度上造成不方便。
因为所有的实例都是共享一组属性,对于包含基本值的属性来说没有问题,但是对于包含引用类型的值来说(例如数组对象),所有的实例都是对同一个引用类型进行操作,那么属性的操作就不是独立的,最后导致读写的混乱。

我们创建的实例一般都是要有属于自己的全部属性的,因此单独使用原型模式的情况是很少存在的。

4. 组合模式

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
}
Person.prototype.sayName = function(){
	console.log(this.name);
}
let person1 = new Person("yk", 18, "software engineer");
let person2 = new Person("ykk", 12, "student");

person1.sayName(); // 'yk'
person2.sayName(); // 'ykk'
console.log(person1.sayName === person2.sayName); // true

优点:采用了构造函数模式和原型模式的优点,这种混成模式是目前使用最广泛,认同度最高的一种创建自定类型的方法。

缺点:由于使用了两种模式,因此对于代码的封装性来说不是很好。

5. 动态原型模式

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

let person1 = new Person("yk", 19, "student");
person1.sayName(); // "yk"

注意在 if 语句中检查的可以是初始化后应该存在的任何属性或方法,不必要检查每一个方法和属性,只需要检查一个就行。

优点:解决了混成模式中封装性的问题

6. 寄生构造函数模式

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

这个模式和工厂模式基本上是一样的,只不过我们是采用 new 操作符最后来创建对象。

注意在构造函数不返回值的情况下,默认会返回新创建的对象,而通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。

优点:可以基于一个已有的类型,在实例化时对实例对象进行扩展。这样既不用修改原来的构造函数,也达到了扩展对象的目的

缺点:不能依赖 instanceof 操作符来确定对象的类型

参考

《JavaScript高级程序设计(第四版)》
JavaScript深入理解之对象创建

你可能感兴趣的:(JavaScript,javascript,设计模式)