虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同 一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用工厂模式的一种变体。

工厂模式

也就是: 用函数来封装以特定接口创建对象的细节,如下列:

  1. function createPerson(name, age, job){
  2. var o = new Object();
  3. o.name = name;
  4. o.age = age;
  5. o.job = job;
  6. o.sayName = function(){
  7. alert(this.name);
  8. };
  9. return o;
  10. }
  11. var person1 = createPerson("Nicholas", 29, "Software Engineer");
  12. var person2 = createPerson("Greg", 27, "Doctor");

BUT, 工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

构造函数模式

ECMAScript 中的构造函数可用来创建特定类型的对象。像 Object 和 Array 这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将前面的例子重写如下:

  1. function Person(name, age, job){
  2. this.name = name;
  3. this.age = age;
  4. this.job = job;
  5. this.sayName = function(){
  6. alert(this.name);
  7. };
  8. }
  9. var person1 = new Person("Nicholas", 29, "Software Engineer");
  10. var person2 = new Person("Greg", 27, "Doctor");

在这个例子中,Person()函数取代了 createPerson()函数。我们注意到,Person()中的代码
除了与 createPerson()中相同的部分外,还存在以下不同之处:

  • 没有显式地创建对象;
  • 直接将属性和方法赋给了 this 对象;
  • 没有 return 语句。

注意⚠️:

要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:

(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。

原型模式

我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中,如下面的例子所示:

  1. function Person(){}
  2. Person.prototype.name = "Nicholas";
  3. Person.prototype.age = 29
  4. Person.prototype.job = "Software Engin"
  5. Person.prototype.sayName = function() {
  6. alert(this.name)
  7. };
  8. var person1 = new Person();
  9. person1.sayName(); //Nicholas
  10. var person2 = new Person();
  11. person2.sayName(); //"Nicholas"
  12. alert(person1.sayName == person2.sayName); //true

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

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实
例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,
但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参
数;可谓是集两种模式之长。下面的代码重写了前面的例子:

  1. function Person(name, age, job){
  2. this.name = name;
  3. this.age = age;
  4. this.job = job;
  5. this.friends = ["Shelby", "Court"];
  6. }
  7. Person.prototype = {
  8. constructor : Person,
  9. sayName : function(){
  10. alert(this.name);
  11. }
  12. }
  13. var person1 = new Person("Nicholas", 29, "Software Engineer");
  14. var person2 = new Person("Greg", 27, "Doctor");
  15. person1.friends.push("Van");
  16. alert(person1.friends); //"Shelby,Count,Van"
  17. alert(person2.friends); //"Shelby,Count"
  18. alert(person1.friends === person2.friends); //false
  19. alert(person1.sayName === person2.sayName); //true

动态原型模式

寄生构造函数模式

稳妥构造函数模式

注: 由于JS创建对象方法太多,常用的也就前4种,不必要全部记忆,但前4种要会用!