阮一峰博客,戳这里
读了这三篇大佬的博客之后,突然顿悟,继承说白了就是对原型链的拷贝(笨笨的),因为这一篇基本扒开了,最后直接赤裸裸的拷贝嘛。
什么是“非构造函数继承”?
var Animal = {
type: 'animal'
}
var cat = {
name: 'po'
}
这里是两个对象,而不是两个构造函数,这样来去继承就是非构造函数的继承,那么该怎么做呢?
利用空对象来继承
先到这里,再再再提一次new做了哪些事,这个在理解继承中很重要!
//例如 new Animal()
var temp = {}
temp.__proto__ = Animal.prototype
Animal.call(temp)
return temp
new主要是将实例的__proto__
指向了上级的prototype
。
然后来看看空对象继承是怎么做的?
function object(obj){
function Fn(){} //创建了一个空函数
fn.prototype = obj //然后将要继承的obj放到空函数的prototype
return new Fn() //然后返回这个空函数的实例
}
知道了上面这两点,我们再来逐步分析
var xxx = object(Animal)
执行object函数,传入Animal,我们知道object函数返回的是里面空函数的实例,所以xxx.__proto__ = Fn.prototype
,然后Fn的prototype又是我们的Animal,原因是这一句fn.prototype = obj
,这样一来xx.__proto__ = Animal
。
做到这个地步之后,xxx没有的属性,就会通过自身的__proto__去上一层去找,就会找到Animal。这样一来就是集成父对象的属性了。
浅拷贝继承
这种方式就直接粗暴的去父对象里面的拷贝,我没有,我就直接从你身上拿就可以了。
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
这样会有一个问题,父对象属性里面是一个对象或者数组的时候机会出现问题,因为是浅拷贝,拷贝的是数组或者对象的引用,这样一来,继承的子对象,如果修改了这类属性,就会对父对象里面响应的属性产生影响,于是我们需要深拷贝。
深拷贝继承
function deepClone(child, father){
var child = child || {}
var toStr = Object.prototype.toString
for(var key in father){
if(father.hasOwnProperty(key)){
if(typeof(father[key]) === 'object' && father[key] !== null){
//Object.assign(child[key] = father[key].prototype.constructor === Array?[]:{}, father[key])
if(toStr.call(father[key]) === '[object, Object]'){
child[key] = {}
}else{
child[key] = []
}
deepClone(child[key], father[key])
}else{
child[key] = father[key]
}
}
}
return child
}
另外,Object.assign是一种介于浅拷贝和深拷贝之间的一种形式,千万不要把它当做深拷贝! 如果是正则、日期什么的就需要另行判断。好了,到这里就差不多了。
总结一下子
其实继承并不是什么难题,无非两种:一种是我没有,就去你身上找;或者是从你身上拿到我的身上,这样我就有了。这样理解之后,基本就通了。最后感谢一波阮大佬~~