没有大白话,有的只是简短而有道理的知识。皮一下,很开心,嘿嘿嘿嘿。
//什末是原型?
原型:一个函数是一个类也是一个对象还是一个方法,只要是函数就会有它自己的prototype属性,这个属性实质是一个指针,指向函数的原型对象,我们把构造函数的原型对象称为对象实例的原型。
函数.prototype = 实例对象.__proto__
构造函数的原型就是原型对象,每一个函数都具有 prototype属性
原型对象.constructor=函数本身
//什末是原型链?
原型链:一个函数的实例对象指向另一个函数的原型对象,那末这个实例对象就会拥有另一个函数的原型上所有的属性和方法。以此类推,就会形成一个链。有点难理解啊,嘿嘿吧。借用一个例子说明白
A.prototype={//原型
constructor:A,
name:'liming',
say:function(){
console.log(1);
}
}
function A(){
this.sex='hhhh'
}
function B(){ }
var b=new B();
b.__proto__=A.prototype;
console.log( b.say,b.name,b.sex);
结果
ƒ (){
console.log(1);
}
"liming"
undefined
由此看出实例对象b可以访问A的原型上的属性和方法。(但是拿不到构造函数A自身的属性和方法)下面介绍解决方法,这样就形成一个链,以此类推,c.__proto__=B.prototype....................
形成一堆堆的链。原型链的顶端为null。
好了,重头戏来了,本文目的就是继承,前期铺垫很多,后面就好理解,继承它有多种实现方法,下面就一一列举它们的利弊
一: 传统形式-->原型链
Grand.prototype.lastName = 'DU';
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
}
var father = new Father();
Son.prototype = father;
function Son(){
}
var son = new Son();
console.log(son.lastName)//DU
缺点:过多的调用了没用的属性,son会继承Father的全部属性还会继承Grand的全部属性,并且如果在Son实例化两个对象(son1,son2)时,改变一个实例化对象(son1)的属性时另一个实例化对象(son2)也会发生改变。
二:借用构造方法
function Person(name,age,sex){
this.name = 'du';
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,grade){
Person.apply(this,arguments);//用apply这个方法前提是这个方法得是new 出来的
//此时的this的原型还是Student.prototype的
this.grade = grade;
}
var student = new Student('liming',18,'女');
console.log(student);
//输出结果:
Student {name: "du",
age: 18,
sex: "女",
grade: undefined
}
缺点是:借用构造方法但只能借用别人的方法不能借用别人的原型,不能继承到原型链上的属性和方法。
三:
Father.prototype.lastName = 'DU';
function Father(){
}
function Son(age){
this.age = age;
}
function inherit(Target,Origin){//传两构造函数
Target.prototype = Origin.prototype;
}
inherit(Son,Father);//先继承
var son = new Son();
var father = new Father();
Son.prototype.sex = 'nv';
console.log(son.lastName,son.sex,father.lastName,father.sex)// DU nv DU nv
缺点:由此得子类继承了父类的属性但子类的私有属性也被父类拿到了,为了改进这一问题引出来下面的方法
四:完美模式
function inherit(Target,Origin){//传两构造函数
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();//接用F()链接一个桥梁
Target.prototype.constructor = Target;//将son的指向归位
Target.prototype.uber = Origin.prototype;//现在Target是继承 new F()但这不是它真正继承的父类而正真继承的父类就是Origin.prototype
}
Father.prototype.lastName = 'DU';
function Father(){
}
function Son(){
}
inherit(Son,Father);
Son.prototype.sex = 'nv';
var son = new Son() ;
var father = new Father();
console.log(son.lastName,son.sex,father.lastName,father.sex);//DU nv DU undefined
这种方式是将父类的prototype传给一个“干净的函数”的原型,再将这个“干净的函数”的原型传给子类的原型,巧妙的用了一个函数进行过渡,这样下来子是子,父是父,间接性的形成继承,但子类在根本上还是继承了父类
值得注意的是在没有加Target.prototype.constructor = Target;这句话时
console.log(son.constructor); //Father()
子类的constructor本应是它函数本身,然而这里打印的却Father()
是因为在底部实现了下面的代码:
son.__proto__->new F() //子类实例化的对象指向了“干净的函数”的实例
new F().__proto__->Father.prototype //“干净的函数”的实例对象指向父类的原型
Father.prototype.constructor ->Father //父类原型的构造器就是父类它自己
所以最后打印出来的就是Father()