JavaScript 中的构造函数及原型模式
今天详细了解了一下 JavaScript 中构造函数和原型模式的相关知识,做了一个总结,以便日后做一个参考。
构造函数
构造函数可以用来创建特定类型的对象。先来看一个构造函数:
1 | function Person(name, age) { |
声明构造函数按照惯例通常以一个大写字母来开头,构造函数的新实例用 new 操作符创建。任何函数用 new 操作符来调用就是构造函数,而构造函数不通过 new 操作符调用也就和普通函数没什么两样。
使用构造函数的缺点是每个方法都需要在每个实例上重新创建一遍。看下面代码:
1 | var xf = new Person("小芳", 20) |
我在这里新建了一个名为 xf 的实例,这时 xm 和 xf 都有一个 sayName() 方法,但这两个方法如果去比较的话却是不一样的。这就说明两个方法是独立的,每个方法都会为其分配一定的内存用来存放,这样显然是浪费资源的。这时就需要提到另一种方法,原型模式。
原型模式
1 | // 构造函数 |
上面我使用了构造函数和原型模式混合的方法,其中 name 属性是构造函数创建的,age 属性和 sayName() 方法是用原型模式(prototype 属性)创建的。原型模式创建的两个方法均为所有实例共享,所以在最后比较两个实例的 sayName() 方法时返回了 true,表示两者是调用的同一个 sayName() 方法。
只要创建了一个新函数,就会为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针,也就是指向构造函数。在上面的例子里 Person.prototype.constructor 指向构造函数 Person。
1 | xf.age = 25 // 重写了小芳的年龄 |
原型链
也就是说,当访问实例的属性或方法时,首先会按照名称查找实例中有没有存在的属性或方法,有的话则屏蔽原型内的同名属性或方法(原型里有的话),直接调用实例中的。如果没有,则向上查找该实例的原型,原型里有的话就调用,都没有的话就返回 undefined。这个步骤是沿着原型链查找的,也就涉及到了原型链的概念。创建了自定义的构造函数之后,其原型对象默认只会取得 constructor 属性,至于其他方法,则都是从 Object 继承而来的。
这么来理解的话,刚刚创建的 xm 实例的原型链是:
1 | xm → Person.prototype → Object.prototype → null |