6.2 创建对象

Object 构造函数和对象字面量都可以用来创建单个对象,但是会产生大量重复代码。

6.2.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;
}

const person1 = createPerson('Mike', 28, 'xxx');
console.log(person1);
person1.sayName();
const person2 = createPerson('Mike', 24, 'aaa');
console.log(person2);
person2.sayName();

缺点:无法识别对象的类型。

6.2.2 构造函数模式

构造函数可以用来创建特定类型的对象。

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

const person1 = new Person('Mike', 28, 'teacher');
console.log(person1);
person1.sayName();
const person2 = new Person('Danie', 24, 'doctor');
console.log(person2);
person2.sayName();

与工厂模式的区别:

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

要创建 Person 的新实例,必须使用 new 操作符。

经历四个步骤:

  • 创建一个新对象
  • 将作用域赋值给这个新对象--因此 this 指向了这个新对象
  • 执行构造函数中的代码--给这个对象添加新属性
  • 返回这个对象

person1 和 person2 都保存着 Person 的不同实例,都有一个 constructor (构造函数)属性,指向 Person。

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

const person1 = new Person('Mike', 28, 'teacher');
console.log(person1);
/**
  Person {name: "Mike", age: 28, job: "teacher", sayName: ƒ}
    age: 28
    job: "teacher"
    name: "Mike"
    sayName: ƒ ()
    __proto__:
      constructor: ƒ Person(name, age, job)  <-- constructor 属性
      __proto__: Object
*/
console.log(person1.constructor); // 打印 person1 的构造函数
/**
  ƒ Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
      console.log(this.name);
    };
  }
 */
console.log(Person.constructor); // 打印 Person 的构造函数
// ƒ Function() { [native code] }

对象的 constructor 属性用来标识对象类型。

如果要检测对象类型,用 instanceof 操作符。

console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true 所有对象均继承自 Object
console.log(Person instanceof Object); // true 所有对象均继承自 Object

缺点:每个实例的方法都要重新创建一遍。实例上的同名方法不相等。

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

const person1 = new Person('Mike', 28, 'teacher');
const person2 = new Person('Danie', 24, 'doctor');
console.log(person1.sayName === person2.sayName); // false <-- 不同实例上的同名方法不相等

创建两个完成相同任务的 Function 实例没有必要,可以像下面这样实现:

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

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

const person1 = new Person('Mike', 28, 'teacher');
const person2 = new Person('Danie', 24, 'doctor');
console.log(person1.sayName === person2.sayName); // true

缺点:在全局定义很多函数,没有封装性。

6.2.3 原型模式

每个实例都有一个 prototype 属性,prototype 是一个指针,指向一个对象,包含所有实例共享的属性和方法。

function Person() {}
Person.prototype.name = 'Mike';
Person.prototype.age = '24';
Person.prototype.sayName = function() {
  console.log(this.name);
};
const person1 = new Person();
const person2 = new Person();
console.log(person1.sayName === person2.sayName); // true
console.log(Person.prototype); // 指向 Person 的原型对象 Prototype
/**
{name: "Mike", age: "24", sayName: ƒ, constructor: ƒ}
  age: "24"
  name: "Mike"
  sayName: ƒ ()
  constructor: ƒ Person()
  __proto__: Object
 */
console.log(person1.__proto__); // 指向构造函数 Person 的原型对象 Prototype
/**
{name: "Mike", age: "24", sayName: ƒ, constructor: ƒ}
  age: "24"
  name: "Mike"
  sayName: ƒ ()
  constructor: ƒ Person()
  __proto__: Object
 */
console.log(Person.prototype === person1.__proto__); // true

可以通过 Object.getPrototypeOf() 来获取原型对象。

console.log(Object.getPrototypeOf(person1));
/**
{name: "Mike", age: "24", sayName: ƒ, constructor: ƒ}
  age: "24"
  name: "Mike"
  sayName: ƒ ()
  constructor: ƒ Person()
  __proto__: Object
 */
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true

通过 hasOwnProperty() 方法可以检测一个属性是存在于对象实例中,还是原型中。

function Person() {}
Person.prototype.name = 'Mike';
Person.prototype.age = '24';
Person.prototype.sayName = function() {
  console.log(this.name);
};
const person1 = new Person();
console.log(person1.hasOwnProperty('name')); // false
person1.age = 18;
console.log(person1.hasOwnProperty('age')); // true

in 操作符在可以访问对象的给定属性时返回 true,无论该属性存在于实例还是原型中。

function Person() {}
Person.prototype.name = 'Mike';
Person.prototype.age = '24';
Person.prototype.sayName = function() {
  console.log(this.name);
};
const person1 = new Person();
console.log('name' in person1); // true

同时使用 hasOwnProperty() 和 in 操作符,可以判断属性是存在于实例中还是原型中。

function hasPrototypeProperty(obj, name) {
  return !obj.hasOwnProperty(name) && name in obj;
}
console.log(hasPrototypeProperty(person1, 'name')); // true
person1.name = 'Gerge';
console.log(hasPrototypeProperty(person1, 'name')); // false

更简单的原型语法

function Person() {}
Person.prototype = {
  name: 'Mike',
  age: 21,
  sayName() {
    console.log(this.name);
  },
};
console.log(Person.prototype);
/**
{name: "Mike", age: 21, sayName: ƒ}
  age: 21
  name: "Mike"
  sayName: ƒ sayName()
  // <-- 缺少了指向 Person 的 constructor
 */
function Person() {}
const person1 = new Person();
console.log(person1.constructor === Person); // true <-- 没有重写 Person.prototype

Person.prototype = {
  name: 'Mike',
  age: 21,
  sayName() {
    console.log(this.name);
  },
};

const person2 = new Person();
console.log(person2.constructor === Person); // false <-- 重写了 Person.prototype

如果 constructor 很重要,可以设置为需要的值。

function Person() {}

Person.prototype = {
  constructor: Person, // <-- 将 constructor 设为 Person
  name: 'Mike',
  age: 21,
  sayName() {
    console.log(this.name);
  },
};
console.log(Person.prototype);
/**
{constructor: ƒ, name: "Mike", age: 21, sayName: ƒ}
  age: 21
  constructor: ƒ Person() <-- constructor 指向了 Person
  name: "Mike"
  sayName: ƒ sayName()
  __proto__: Object
  */
const person2 = new Person();
console.log(person2.constructor === Person); // true <-- person2 的构造函数指向了 Person

缺点:

  • 缺少传入初始参数,实例默认情况下获取相同属性。
  • 共享,引用类型的值会一起改变
Last Updated: 7/23/2019, 10:51:21 AM