构造函数与实例对象的关系

function Person (name, age) {
  // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
  // var instance = {}
  // 然后让内部的 this 指向 instance 对象
  // this = instance
  // 接下来所有针对 this 的操作实际上操作的就是 instance

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

  // 在函数的结尾处会将 this 返回,也就是 instance
  // return this
}

 

使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型。在每一个实例对象中的_proto_中同时有一个constructor属性,该属性指向创建该实例的构造函数:

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

对象的constructor属性最初是用来标识对象类型的,但是,如果要检测对象的类型,还是使用instanceof操作符更可靠一些:

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true

总结:

1. 构造函数是根据具体的事物抽象出来的抽象模板
2. 实例对象时根据抽象的构造函数模板得到的具体实例对象
3. 每一个实例对象都具有一个constructor属性,指向创建该实例的构造函数
  - 注意:constructor是实例的属性的书法不严谨
4. 可以通过实例的constructor属性来判断实例和构造函数之间的关系
  - 注意:这种方式不严谨,推荐使用instanceof操作符
 

构造函数的问题

使用构造函数带来的最大好处就是创建对象方便了许多,但是其根本也存在一个内存浪费的问题:
function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = function () {
    console.log('hello ' + this.name)
  }
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

在该示例中,从表面上好像没有什么问题,但是实际上这样有一个很大的弊端。就是对于每一个实例对象,type和sayHello都是一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

console.log(p1.sayHello === p2.sayHello) // => false

对于这种问题我们可以把需要共享的函数定义到构造函数外部:

function sayHello = function () {
  console.log('hello ' + this.name)
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = sayHello
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true

这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。

var fns = {
  sayHello: function () {
    console.log('hello ' + this.name)
  },
  sayAge: function () {
    console.log(this.age)
  }
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = fns.sayHello
  this.sayAge = fns.sayAge
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。 但是代码看起来还是那么的格格不入,那有没有更好的方式呢

小结:

1. 构造函数语法
2. 分析构造函数
3. 构造函数和实例对象的关系
4. 构造函数的问题