前言
JS是面向对象的弱类型的语言,这对于js来说是一个非常大的特性之一。JS有属于自己的三大特性五大原则。
三大特性
- 封装
- 继承
- 多态
五大原则
- 单一职责原则
- 开发封闭原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
在这里我主要概述下对 继承 的认识和简单的应用,其实继承主要的实现方式也是通过原型链的继承来继承父类的属性和方法的,也可以通过拷贝来复制一份属于自己的属性和方法
浅拷贝
浅拷贝就是直接将父类的属性和方法通过for..in来循环复制到子类,那么子类就拥有了父类的所有方法。
缺点:如果父类中有子对象或数组的时候,那么就会出行问题,需要通过递归来解决,这也是深拷贝到来的意义了,浅拷贝详细看代码//浅拷贝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=china
或obj.arr[0]=88
的时候,那么父类的born和arr也会被修改,因为在for..in里面复制给子类的是一个应用而已,所以子类和父类引用的是同一个对象和数组。那么就需要用深拷贝的方式来解决了。
深拷贝
深拷贝相比于浅拷贝就是,深拷贝会将父类的子对象或数组通过用递归的方式来复制到子对象里面去。代码如下
//深拷贝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那么肯定是引用类型的值 //[object Array]说明是数组,如果是[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来实现继承父类的属性和方法,call和apply可以参考
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可以将对象的所有属性和方法继承给子类
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,但是在这里有个问题就是子类自己的东西会暴露给父类
function parent(){}function child(){}child.prototype=parent.prototypevar obj=new child()//这样的话子类就能访问到父类prototype里面的属性和方法了//但是如果我们给子类child的原型prototype添加一个属性或方法child.prototype.xx='xxx',并且实例父类var g=new parent(),访问`g.xx`返回的结果是xxx,这样的话就是子类的东西暴露了给父类,这样是不行的复制代码
所以不能通过这种方式来实现继承
- 第二种能够解决第一种方法出现的问题,但是又有新的问题就是会创建出一个多余的对象,并且会将父对象的不在原型上的属性和方法也同时复制到新创建的对象上。对象的作用主要是用来做桥接
function parent(){)function child(){}child.prototype=new parent();//这里创建的这个对象会将parent里面不在prototype上面的属性和方法复制到这个对象上面,这样如果parent的数据很大的情况下就会占用内存,造成性能方面上的问题var obj=new child();复制代码
那么就需要另外一种新的方式来解决这个问题
- 第三种方式就是通过创建出一个新的函数,并且将子类的prototype等于新创建的函数的实例,其实这种方式也是Object.create的实现方式
function parent(){}function child(){}funcion F(){}F.prototype=parent.prototypevar f=new F();child.protype=f;var obj=new child();复制代码
这种方式是前面两种方式的结合体,借助了第一种方式F.prototype=parent.prototype来建立关系,借助了第二种方式创建桥接的方式来建立关系,所以这种方式解决了前面出现的问题