什么是原型
关于构造函数
什么是构造函数
在JS当中,函数分为构造函数和普通函数,而构造函数与普通函数的区别就是可以通过new关键字来创造新的实例。
eg:
function person(){
}
var person1 = new person()
构造函数的缺点
由于将所有的属性和方法都写在了构造函数当中,所以每创建一个新的实例就会将每个属性都创建一遍,但是往往我们在创建构造函数时,对于每个实例会具有相同的属性和方法。这也就造成了极大的内存的浪费。
原型(prototype)
我们所创建的每个函数都有个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象就是原型对象。
对于每个实例来说,也拥有它的prototype属性,它指向该实例构造函数的原型对象
原型对象
由于构造函数浪费内存的缺点,所以存在了原型对象这一概念。
原型对象的用途就是包含所有实例所共享的属性和方法的对象。
构造函数属性(constructor)
只要创建了一个新函数,就会对应的创建一个prototype属性,这个属性指向该函数的原型对象,而每个原型对象都会拥有一个constructor(构造函数)属性,这个属性包含一个指针,该指针指向prototype属性所在的构造函数。
构造函数与prototype和constructor关系
小栗子:
function person(){
}
person.prototype.name = "Nicholas";
person.prototype.age = 29;
person.prototype.job = "Software Engineer";
person.prototype.sayName = function (){
alert(this.name);
}
var person 1 = new person();
var person 2 = new person();
对于这个构造函数,我们来分析他的原型以及constructor属性。

prototype的相关方法
isPrototypeOf()
用这个方法来判断某个prototype对象与某个实例之间是否有关系
object.prototype.isPrototypeOf(实例名)
hasOwnProperty()
每个实例都有hasOwnProperty()方法,用来判断某一个属性到底是本地属性还是继承自prototype对象的属性
实例名.hasOwnProperty("属性名")
是本地属性则为true,否则则为flase
in运算符
in运算符可以用来判断某个实例是否含有某个属性,不论是本地属性还是继承自prototype的属性
"属性名" in 实例名
是则返回true,否则返回flase
还可以用来遍历某个对象的所有属性
eg:
for(var per in person)
<!--在for循环内遍历整个person对象,将所包含的属性一一赋给per-->
什么是原型链
除了object的原型是null外,其余所有的对象和原型都有自己的原型,对象的原型指向原型对象。在JS中,往往具有多个层级,多个原型的层层嵌套就构成了原型链。
在查找一个对象的属性时,倘若在当前对象找不到该属性,则会沿着原型链一直往上查找,直至找到为止,若查找完整个原型链都没有找到,则返回undefined。
-proto-属性
对于所有的JS对象而言,都具有一个-proto-属性。-proto-属性指向实例对象的构造函数的原型。
而原型链的形成就是-proto-实现对象与对象之间的连接,直至最后指向null。
- Object是作为众多new出来的实例的基类
- Function是作为众多function出来的函数的基类
- 构造函数的proto都指向Function.prototype
- 原型对象的proto都指向Object.prototype
- Object.prototype的proto指向null
注:
- Object可以看为所有对象的集合
- Function可以看为所有函数的集合
- 函数也是一种特殊的对象
小栗子(原型链)
function Foo(){
...
}
let f1 = new Foo()

分析:
f1是Foo()的实例,所以f1的-proto-指向Foo的原型,也就是Foo.prototype。
Foo.prototype是Foo的原型,所以它的-proto-指向Object.prototype。
Object.prototype的-proto-指向null。
Foo()是构造函数,他的-proto-指向Function.prototype
Function.prototype是函数的原型,则指向Object.prototype。
Object.prototype的-proto-则最终指向null
原型的作用
- 实现数据的共享,节约内存空间
- 实现继承
节约空间
通常我们将构造函数的公共属性放置在原型当中,这样就节省了内存空间,防止了没新建一个实例就新建许多重复的属性的问题。
关于继承
关于对象之间的继承涉及到了原型链的继承和其他方式的继承,在这里一并整理。
原型链继承
对于每一个构造函数来说,都是一个prototype属性,他类似于一个指针,指向另一个对象,对于这一个对象中的所有内容,该构造函数的每一个实例都会继承。所以一般将构造函数中的公共属性以及方法写入这个对象当中,这样可以节省内存并提高效率。而对于每一个prototype都有他对应的constructor属性,指向他的构造函数。每个实例也有一个constructor属性,他指向prototype的constructor值。
原型链的继承其实就是使子对象的prototype与父对象的一个实例的prototype相等。由于这种相等之后的子对象的constructor指向了父对象,所以为了保持一致,在修改了子对象的prototype之后,应该紧接着修改子对象prototype的constructor值,使子对象整体保持一致。 小栗子:

构造函数继承
使用构造函数的继承即是使用call或apply的方法,将父构造函数绑定早子对象上,相当于在子对象中添加一行内容。 小栗子:

组合继承
组合继承,顾名思义,即是将构造函数继承与原型链继承组合使用,这样的继承结果包含了两种继承方式的特点,是较为常用的一种继承方式。 小栗子:

原型式继承
原型式继承实际就是建立了一个空对象作为中间量,我们使这个空对象的prototype等于父对象的prototype,再使子对象的prototype等于这个空对象的一个实例,达到了继承的目的。我们一般在使用的过程中,将这种方法封装为一个函数,方便我们在使用的过程中对其进行调用。 小栗子:

参考博客
www.ruanyifeng.com/blog/2010/0…
www.ruanyifeng.com/blog/2010/0…
以及《Javascript高级程序设计》p147起