学习一波阮一峰的博客 戳这里
博客中是自己的理解,以及对大佬描述不清楚的地方进行了修正,也算是自己的一个再(xiao)产(tu)出(cao)吧
构造函数进行继承
先来看个简单的:
function Animal(){
this.type = 'animal;'
}
function Cat(name, color){
this.name = name
this.color = color
//这里用call,个人觉得更好些
Animall.call(this)
}
var cat = new Cat('po', 'orange')
console.log(cat.type) //animal
创建了一个Animal和Cat构造函数,然后在Cat里面调用Animal的构造函数,在将Cat实例化,就可以访问到Animal的属性了。 这个例子显然有问题,放在第一个就是用来找茬的。
什么问题呢?假如我想使用在Animal上的公共方法,像这样Animal.prototype.eat = function(){ console.log('animal eat') }
,用cat.eat()
是访问不到的。
为什么呢?因为我们Cat和Animal的原型根本就没有关联起来呀。你看看咱们上面的代码,那个地方关联过?
使用原型进行继承
那下面我们就将两者的原型关联起来试试看
function Animal(){
this.type = 'animal;'
}
Animal.prototype.eat = function(){ console.log('animal eat') }
function Cat(name, color){
this.name = name
this.color = color
}
Cat.prototype = new Animal()
var cat = new Cat('po', 'orange')
console.log(cat.type) //animal
console.log(cat.eat()) //animal eat
这种方法好!可以拿到属性和方法,一举两得。但是,这里有个陷阱(keng)!!!Cat.prototype = new Animal()
之后,我们的Cat.prototype里面的所有方法都消失了!这是怎么回事?因为new,new做了四件事,这里再次回顾一下:
var temp = {}
temp.__proto__ = Animal.prototype
Animal.call(temp)
return temp
看到了吗?new会使用一个空对象并且将其返回。这样一来我们的Cat.prototype就被清空了(包括自身的constructor)。所以我们需要自己多做一件事Cat.prototype.constructor = Cat
。
如果我们不这样做,那么Cat.prototype的构造函数会是什么?我们在去看看new做的第二件事,这个空对象指向了Animal.prototype,所以Cat.prototype自身如果没有constructor属性的话就会去Animal.prototype上面去找。Cat.prototype.constructor === Animal //true
所以,如果我们替换了prototype,需要手动去纠正它。
直接继承prototype
既然上面这种方法有坑,而且的的确确让你很容易漏掉,那我们改进一下:
function Animal(){}
Animal.prototype.eat = function(){ console.log('animal eat') }
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
var cat = new Cat('po', 'orange')
console.log(cat.eat()) //animal eat
既然我们想从Animal.prototype上面那东西,直接从上面拿不就行了?而且我还机智的填了上面会出现的坑。同时结果也是我想要的。
但是!!我们的Cat.prototype和Animal.prototype指向的是同一个原型,这会导致我在Cat.prototype上做了什么事,会同时发生在Animal.prototype上。这是为什么呢?MDZZ,这两个就是同一个东西呀,原型是堆中一块内存,Cat和Animal都指向这块内存,操作的是同一个东西,怎么会不影响?
与此同时,自以为聪明的填坑Cat.prototype.constructor = Cat
,此时的Cat和Animal的原型是同一个,修改了constructor之后,导致Animal的constructor变成了Cat。这种方法果断PASS。。。
利用空对象作为中介
var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
我们使用中间对象进行过度,巧妙的将Cat.prototype和Animal.prototype解耦,这样就不会出现问题了。仔细观察会发现这个和new做的事情有异曲同工之处。
我们进行封装一下在使用
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
extend(Cat,Animal);
var cat1 = new Cat('po' 'orange');
alert(cat1.eat()); // animal eat
这里Child.uber = Parent.prototype
的意思类似于__proto__
。
拷贝继承
这里还有一种简单粗暴的方式
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
function Animal(){}
Animal.prototype.eat = function(){ console.log('animal eat') }
extend2(Cat, Animal);
var cat1 = new Cat('po' 'orange');
alert(cat1.eat()); // animal eat
直接遍历父类的原型,一个个复制到子类原型上即可。
感慨一下,大佬还是大佬呀。。。