一、 原型链
_proto_ : 原型链,对象的属性,指向生成该对象的构造函数的原型对象
prototype: 原型对象,函数(构造函数)特有的属性,存储共享的属性和方法
// 所有的javascript对象都会继承一个prototype原型对象 继承原型对象上的属性和方法
// Date 对象 从Date.prototype 原型对象上继承属性和方法
// Array 对象 从Array.prototype 原型对象上继承属性和方法
以js数组对象Array为例:
var arr = new Array();
console.log(typeof Array);
//输出function,即Array本质是一个函数,js中函数也是对象
console.log(arr.__proto__);
//输出的是Array(arr的上一层,即创建arr的构造函数)的原型对象
console.log(arr.__proto__.__proto__);
//输出的是Object(即Array的上一层)的原型对象
console.log(arr.__proto__.__proto__.__proto__);
//输出为null Object为最高层 没有继承任何对象
原型链与圆形函数的关系,通过下面的例子作以说明
//定义一个Person 父类
function Person() {
this.color = "";
this.sleep = function () {
}
}
//给Person类添加原型对象
Person.prototype = {
constructor: Person,
name: "",
sex: "",
eat: function () {
return "eat";
}
}
//定义一个子类Child
function Child() {
this.job = "";
}
// 子类 原型继承 父类
Child.prototype = new Person(); //先继承,操作子类构造函数的prototype属性
var c = new Child(); //再实例化子类对象
console.log(c.__proto__); //输出父类
console.log(c.__proto__.__proto__);//输出父类的原型对象
console.log(c.__proto__.__proto__.__proto__); //输出Object 原型对象
console.log(c.__proto__.__proto__.__proto__.__proto__);//输出null
/*下面这种继承没有意义,之前自己在练习过程中上下两种输出一致,对此理解有误,之后发现了问题*/
/*×××该方法请勿采用×××*/
var c1=new Child(); //先实例化对象
c1.__proto__=new Person(); //再继承,操作子类实例化对象的"__proto__"对象
console.log(c1.__proto__); //输出父类
console.log(c1.__proto__.__proto__);//输出父类的原型对象
console.log(c1.__proto__.__proto__.__proto__); //输出Object 原型对象
console.log(c1.__proto__.__proto__.__proto__.__proto__);//输出null
/*两个实例化对象以上的输出一致*/
console.log(Child.prototype == c.__proto__); //输出true
/* 第一种实例化:
* Child的prototype 通过 Child.prototype = new Person() 设置为了 Person
* 因此Child实例化后的对象的__proto__也为person
*/
console.log(Child.prototype == c1.__proto__); //输出false
/*第二种实例化:
*先实例化后再继承,只对实例化后的c1对象进行了 c1.__proto__=new Person()的操作
*因此,Child的prototype未曾设置,c1的__proto__为person;
*/
总结:prototype为函数特有的,__proto__是对象的属性
构造方法. prototype== 实例对象.__proto__
二、继承
- 原型链继承:将父类的实例化对象赋给子类的原型对象
//原型链继承
//定义一个父类
function Person(name,age){
this.name=name;
this.age=age;
this.eat=function(){
return this.name+"吃饭";
}
}
//为父类增添原型属性
Person.prototype.color="黄种人";
//为父类增添原型属性方法
Person.prototype.sleep=function(){
return this.name+"睡觉";
};
//定义一个子类
function Child(sex){
this.sex=sex;
this.drink=function(){
return this.name+"喝水";
}
}
//将父类Person的实例化对象 赋给子类Child的原型对象prototype
Child.prototype=new Person("张三","20");
//实例化子类对象
var child = new Child("男");
//输出自身、父类、父类原型所有的属性及方法
console.log(child);
//判断child对象是否为构造类Child Person的实例化对象
console.log(child instanceof Child); //true
console.log(child instanceof Person); //true
优点:
1.实例即是子类的实例,也是父类的实例
2.子类本身,父类本身,父类原型,父类原型新增的所有属性方法,子类都可以访问到
缺点:
1.子类实例无法直接向父类构造方法传参;
2.继承之后原型对象上的属性全部是共享的
3.不能多继承 ,即一个子类不能继承多个父类
2.构造继承:通过call apply将父类属性及方法完全复制给子类
//定义父类Person
function Person(name,sex){
this.name=name;
this.sex=sex;
this.eat=function(){
return this.name+"吃饭";
}
}
//定义父类 Country
function Country(con){
this.con=con;
}
//添加父类原型属性
Country.prototype.fn="父类原型"; //子类不能继承
//定义子类
function Child(age,n,s,c){
this.age=age;
Person.apply(this,[n,s]); //继承Person父类
Country.call(this,c); //继承Country父类
}
var child=new Child(20,"张三","男","China");
console.log(child);
console.log(child instanceof Child); //true
console.log(child instanceof Person); //false
console.log(child instanceof Country); //false
优点:
1.可以多继承
2.子类实例可向父类构造函数传参
缺点:
1.构造继承只能call apply
2.子类只能继承 父类的构造属性和方法,不能继承父类原型的属性和方法
3.实例对象只是子类的实例,不是父类的实例
补充:call() 与 apply()方法
每个函数都包含两个非继承而来的方法:call() 与 apply()
两方法作用均为:改变函数体内部 this 的指向
call(thisObj,参数1,参数2,...);
apply(thisObj,[参数1,参数2,...]);
3.实例继承:在子类里直接实例化父类(子类的实例返回 给父类添加的属性和方法)
function Person(name,sex){
this.name=name;
this.sex=sex;
this.eat=function(){
return this.name+"吃饭";
}
}
Person.prototype.fn="父类原型"; //子类不能继承
function Child(n,s){
var child=new Person(n,s); //子类内部实例化父类
return child;
}
var child=new Child("张三","男");
console.log(child); ////直接输出实例化后的Person对象
console.log(child instanceof Child); //false
console.log(child instanceof Person); //true
优点:
1.可以传递给父类参数
2.不限制调用的方式
缺点:
1.不能多继承
2.实例是父类的实例,不是子类的实例
4.拷贝继承:将父类对象中的每个属性和方法遍历复制到子类的原型中
//父类Person
function Person(name){
this.name=name;
this.eat=function(){
return this.name+"吃饭";
}
}
//父类Country
function Country(con){
this.con=con;
}
function Child(age,n,c){
this.age=age;
var per=new Person(n);
var per1=new Country(c);
for(var p in per){
Child.prototype[p]=per[p]; //将父类实例的属性和方法复制给资料类的原型
}
for(var key in per1){
Child.prototype[key]=per1[key];
}
}
var child=new Child(20,"张三","china");
console.log(child);
console.log(child instanceof Child); //true
console.log(child instanceof Person); //false
console.log(child instanceof Country); //false
实例是子类的实例,不是父类的实例
优点:
1.可以多继承
2.子类可以向父类传递参数
缺点:
1.效率较低,内存占用高
5.组合继承:原型链继承+构造继承
//父类Person
function Person(name) {
this.name = name;
this.sleep = function () {
return this.name + "睡觉";
}
}
//父类 Country
function Country(con){
this.con=con;
}
function Child(sex,n,c){
this.sex=sex;
Person.call(this,n); //可多继承
Country.call(this,c);
}
Child.prototype=new Person();
//可直接向父类传参,弥补了原型链继承的缺点
var child = new Child("男","张三","China");
console.log(child);
console.log(child instanceof Child); //true
console.log(child instanceof Person); //true
console.log(child instanceof Country);
// 子类没有通过原型链继承Country false
优点:
1.实例可以直接向父类传参
2.没有原型对象属性的共享
3.可多继承
4.实例是子类的,也是父类的
缺点:
调用了两次父类的构造函数
6.寄生组合继承:将父类的原型给予一个空对象的原型
//父类
function Person(name) {
this.name = name;
this.sleep = function () {
return this.name + "睡觉";
}
}
//子类
function Child(n, age) {
this.age = age;
this.eat = function () {
return this.name + "吃饭"
}
Person.call(this,n);
}
//寄生
(function(){
var fn=function(){
};
//将父类的原型对象给予空对象
//避免两次调用父类的构造函数
fn.prototype=Person.prototype;
Child.prototype=new fn();
})();
var child=new Child("张三",20);
console.log(child);
console.log(child instanceof Child); //true
console.log(child instanceof Person);//true
优点:
处理了组合继承的缺点 ,避免两次调用父类的构造函数
实例是子类的,也是父类的
来源:CSDN
作者:奢คิดถึง
链接:https://blog.csdn.net/pet_ch/article/details/104775055