在JavaScript中,每一个函数都有一个prototype(原型)属性,该属性是一个对象,它的作用是使特定类型的所有对象实例可以共享它所包含的属性和方法。
原型的创建分为四种状态,我们用例子来分别说明:
第一种状态:定义一个函数
function Person(){};
在内存中会为Person函数分配一块空间,在这个空间中有一个prototype属性,另外还会为该函数创建一个原型对象,在原型对象中有一个constructor属性。Person函数和它的原型对象的关系如下图中所示。我们称这时的内存模型为原型的第一种状态。
第二种状态:为函数的prototype属性定义变量
Person.prototype.name = "Leon";
Person.prototype.age = 22;
Person.prototype.say = fucntion(){
alert(this.name + "," + this.age);
}
此时,通过原型添加的方法和属性都被存储在原型的内存空间中。内存模型如下:
第三种状态: 通过new关键字来创建Person对象
var p1 = new Person();
p1.say();
我们通过new Person()创建了一个Person对象p1,此时会在内存中为p1对象分配一块内存空间,在p1的内存空间中会有一个_proto_内部属性,这个内部属性是不能被访问的,它也指向Person原型,内存模型如下:
第四种状态:再创建一个Person对象p2,并且将p2对象的name属性修改
var p2 = new Person();
p2.name = "Ada";
p2.say();
当创建对象p2的时候,同样会在内存中为它分配空间,在p2对象的空间中也会有一个_proto_内部属性指向Person的原型。
当我们通过p2.name = "Ada";为对象p2的name属性赋值的时候,JavaScript会在p2的内存空间中设置自己的name属性,并将值设置为“Ada。 内存模型如下:
我们知道在JavaScript中什么都是对象,但是对象主要分为两类:普通对象(Object)和函数对象(Function)。
理解了这两种对象也就能更好的理解原型对象和原型链。
function Animal(){}
Animal.prototype存储的值就是原型对象,该对象可以理解为Animal的一个实例。也就是在函数对象声明的时候创建的,
并且赋值给prototype。
var animal = new Animal();
animal.prototype的输出值为undefined,因为animal是一个实例对象,prototype属性只存在于函数对象中。
但是animal该实例对象还是有__proto__属性的,该属性指向的是构造函数所在的函数对象的prototype。
我们在实现继承时,可以将Animal的prototype值设置为父类的实例对象,这样子类就有了父类的所有属性和方法。
因为我们在查找一个属性时,会现在实例对象上去找,如果没有的话就会在prototype去找。所以,prototype上的属性和方法
是所有实例对象所共有的,有一个实例对象修改该了值,其他示例对象也是可以看到的。
这样,因为实例对象没有prototype属性,所以如果自身实例对象没有属性时,会基于__proto__到prototype上去找。
每个对象都有__proto__属性,该属性的链接就构成了原型链。