继承之浅拷贝和深拷贝

js是面向对象的弱类型的语言,对于js来说也是一个非常大的特性之一,其实继承主要的实现方式也是通过原型链的继承来继承父类的属性和方法的,也可以通过拷贝来复制一份属于自己的属性和方法。接下来就让我们说说继承的主要的几种方式。

浅拷贝

浅拷贝就是直接将父类的属性和方法通过for..in来循环复制到子类,那么子类就拥有了父类的所有方法。
缺点:如果父类中有子对象或数组的时候,那么就会出行问题,需要通过递归来解决,这也是深拷贝到来的意义了,详细看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//浅拷贝
var parent={
name:'chen',
age:20,
where:{born:pn,study:sz}
arr:[1,2,4]
}
var child={}
//以下是主要的实现方法
function extend(parent,child){
 var child=child||{}; //如果child是undefind的情况下,那么将child设置为空对象
 for(var prop in parent){
   child[prop]=parent[prop]; //在子对象里面开始添加属性和方法
 }
 return child;
}
var obj=new extend(parent,child)

注:这里出现的问题是,当执行obj.name可以获取到chen,这是正确的,但是当执行obj.where.born=chinaobj.arr[0]=88的时候,那么父类的born和arr也会被修改,因为在for..in里面复制给子类的是一个应用而已,所以子类和父类引用的是同一个对象和数组。那么就需要用深拷贝的方式来解决了。

深拷贝

深拷贝相比于浅拷贝就是,深拷贝会将父类的子对象或数组通过用递归的方式来复制到子对象里面去。代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//深拷贝
var parent={
name:'chen',
age:20,
where:{born:pn,study:sz}
arr:[1,2,4]
}
var child={}
//以下是主要的实现方法
function extendDeep(parent,child){
 var child=child||{}; //如果child是undefind的情况下,那么将child设置为空对象
 for(var prop in parent){
   if(typeof parent[prop]==='object'){ //因为数组和对象都属于对象,所以只要检测到时object那么肯定是引用类型的值
child[prop]=(Object.prototype.toString.call(parent[prop])=='[object Array]')?[]:{};
     entendDeep(parent[prop],child[prop]);
   }
   child[prop]=parent[prop]; //在子对象里面开始添加属性和方法
 }
 return child;
}
var obj=new extend(parent,child)

这种拷贝的方式完美解决了浅拷贝的问题

函数继承

函数继承可以通过call或apply来实现继承父类的属性和方法

1
2
3
4
5
6
7
8
9
function parent(){
this.name='chen';
this.age={now:12};
}
function child(parent){
  parent.call(this); //相当于parent.parent()接着就可以调用里面的name和age了
  this.lang='english';
}
var obj=new child();

create实现继承

通过Object.create可以将对象的所有属性和方法继承给子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var parent={name:'chen'}
function child(p){
function f(){};
f.prototype=p;
f.prototype.constructor=f;
var ins=new f();
return ins;
}
 //上面的代码相当于
 var parent={name:'chen'}
function child(p){
var ins=Object.create(p)
return ins;
}
  //不过Object.create在低级浏览器是不能使用的,ie>9

类的继承

就是最普通的通过原型链来实现子类继承父类,继承的主要过程就是创建父类+创建子类+继承的实现方式

  • 第一种是直接将子类的child.prototype=parent.prototype,但是在这里有个问题就是子类自己的东西会暴露给父类
    1
    2
    3
    4
    5
    6
    function parent(){}
    function child(){}
    child.prototype=parent.prototype
    var obj=new child()
    //这样的话子类就能访问到父类prototype里面的属性和方法了
    //但是如果我们给子类child的原型prototype添加一个属性或方法child.prototype.xx='xxx',并且实例父类var g=new parent(),访问`g.xx`返回的结果是xxx,这样的话就是子类的东西暴露了给父类,这样是不行的

所以不能通过这种方式来实现继承

  • 第二种能够解决第一种方法出现的问题,但是又有新的问题就是会创建出一个多余的对象,并且会将父对象的不在原型上的属性和方法也同时复制到新创建的对象上。对象的作用主要是用来做桥接
    1
    2
    3
    4
    function parent(){)
    function child(){}
    child.prototype=new parent();//这里创建的这个对象会将parent里面不在prototype上面的属性和方法复制到这个对象上面,这样如果parent的数据很大的情况下就会占用内存,造成性能方面上的问题
    var obj=new child();

那么就需要另外一种新的方式来解决这个问题

  • 第三种方式就是通过创建出一个新的函数,并且将子类的prototype等于新创建的函数的实例
    1
    2
    3
    4
    5
    6
    7
    function parent(){}
    function child(){}
    funcion F(){}
    F.prototype=parent.prototype
    var f=new F();
    child.protype=f;
    var obj=new child();

这种方式是前面两种方式的结合体,借助了第一种方式F.prototype=parent.prototype来建立关系,借助了第二种方式创建桥接的方式来建立关系,所以这种方式解决了前面出现的问题

多态

一个引用类型(变量)在不同状态下的多种类型。多态用途在面向对象的开发的时候,需要一个方法不变,但是他的参数是需要变化的,就可以使用多态

1
2
3
4
5
6
7
8
9
function DuoTai(){
var dom=document.getElementById('box');
if(arguments.length==0){
return dom.style.fontSize;
}else{
   dom.style.fontSize=arguments[0]+'px';
}
}
//解释:当我传递参数的时候,那么我就可以自行字体大小设置,如果没有传递参数的时候直接返回字体大小

重写
重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载,这也是重载和重写的区别,上面那个DuoTai的函数就是重载的例子,同样的方法,但是参数可能不一样

1
2
3
4
5
6
7
8
9
10
11
12
function parent(){}
parent.prototype.run=function(){
console.log('parent runing');
}
child.prototype=Object.create(parent.prototype);
child.prototype.constructor=child;
child.super=parent.prototype; //这个方法是为了在子类中如果有与父类同名的方法或属性的时候仍然可以使用父类的属性和方法而不会被覆盖
function child(){}
child.prototype.run=function(){
console.log('child runing');
child.super.run();//这个是调用父类parent中的run方法
}

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器