原型、原型链、继承
构造函数,实例,原型与原型对象之间的关系:
构造函数有它自己的属性及其方法,其中包括自己定义的属性和方法外,还有两个特殊属性(prototype、constructor);而每个他的实例都会拥有它的所有属性和方法(包括prototype、constructor)constructor则是指向每个实例的构造函数,而prototype 原型 则是一个地址指向原型对象,这个原型对象创建了实例后,只会取得constructor属性,其他的都是从Object继承而来;在Firefox 、 chrome在对象上都支持一个属性"proto";这个原型对象的属性和方法是所有该类实例共享的任何该类实例够可以访问该原型对象的属性和方法
原型对象属性和方法的三个方式:
1.通过Person.prototype 属性
console.log(Person.prototype.name);//输出----->person
2.通过 属性屏蔽 delete (屏蔽构造函数属性或者方法)
p1.sayName(); //输出----->构造函数对象
delete p1.name;
console.log(p1.name); //输出----->原型属性
delete p1.sayName;
p1.sayName(); //输出 —>原型对象方法
3.通过Object.getPrototypeOf(p1)
console.log(Object.getPrototypeOf(p1).name);//输出----->原型属性
上面我们需要注意就是当实例调用属性或者方法时,有一个”属性搜索机制“,所谓”属性搜索机制“就是当实例访问属性或者方法时首先会现在自身的实例中搜索,看是否有对应属性,有,则返回;如果没有那么它会通过prototype 到原型对象中寻找对应的属性和方法;
原型链:
其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。我们知道,每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,而实例又包涵一个指向原型对象的内部指针。
如果我们让原型对象(A.prototype) = 另一个类型的实例(new B()),那么,该原型对象(A.prototype)就有一个指向另一个原型对象(B.prototype)的指针,相应的,另一个原型对象(B.prototype)也包含指向另一个构造函数(B)的指针。 ------------->如果另一个的原型(B.prototype)又是另一个类型(C)的实例,上诉关系依然成立,就构成了实例与原型的链条,这就是原型链。
实现原型链的基本方法:
function Person () {
this.name = “person”;
}
Person.prototype.getPersonName = function () {
return this.name;
};
function Student () {
this.studentname = “student”;
}
// 继承了Person
Student.prototype = new Person();
Student.prototype.getStudentName = function () {
return this.name;
};
var stu = new Student();
console.log(stu.getPersonName()); //person
如上就是通过将 Student()的prototype = new Person() 即子类的原型对象等于父类的实例,从而Student.prototype有了Person的所有属性和方法,实现了继承。通过实现原型链,再结合 “属性搜索机制“,则
stu.getPersonName()
会经过三个阶段:1.搜索实例 2.搜索Student.prototype 3.搜索Person.prototype;如果再没就会到Object 对象的prototype对象上寻找。因为所有的function和对象等引用类型都继承Object;这也就说明了为什么不是通过Object直接实例的对象(自定义类型)会有valueof(),toString()等方法。
基于原型链实现继承
基于原型链实现继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。在前面我们已经介绍了原型,构造函数和对象实例之间的关系,并详细的分析了它们的内存模型结构。我们通过下面的例子来分析JavaScript基于原型链实现继承的方法。
// 创建父类
function Parent(){
this.parentValue = “Parent”;
}
// 在父类的原型中添加方法
Parent.prototype.showParentValue = function(){
alert(this.parentValue);
}
// 创建子类
function Child(){
this.childValue =“Child”;
}
// 实现继承,让子类Child的原型链指向Parent对象
Child.prototype = new Parent();
// 在子类的原型中添加方法
Child.prototype.showChildValue = function(){
alert(this.childValue);
}
// 创建子类对象
var c = new Child();
// 子类对象调用继承自父类的方法
c.showParentValue();
// 子类对象调用自己的方法
c.showChildValue();
在上面的代码中,我们首先创建了一个父类Parent,并在它的原型中添加了showParentValue方法。
接着我们创建了一个子类Child,并通过让子类的原型链指向父类来实现继承。
/** 实现继承的关键代码 **/
Child.prototype = new Parent();
接着在子类的原型链中添加showChildValue方法。然后创建了一个子类对象,这时的子类对象c既可以调用自己的方法,也可以调用继承自父类的方法。
在执行上面的代码之后,子类和父类的内存模型图如下图所示: