今天详细了解了一下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