JS 是如何实现继承的
1、原型链
基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法。
原型链算是实现继承的一个简单又比较好理解的一个方法了,对于原型链不理解的可以先去看看彻底理解 JS 的原型和原型链。具体实现如下:
<script> function Parent(){ this.a = 66; this.arr = [11]; } function Children(){ } Children.prototype = new Parent();//核心 var child1 = new Children(); var child2 = new Children(); console.log(child1.a);//66 console.log(child2.a);//66 //修改基本数据类型 child2.a = 88; console.log(child1.a);//66 console.log(child2.a);//88 //修改引用类型 child2.arr.push(22); console.log(child1.arr);//[11,22] console.log(child2.arr);//[11,22] </script>
可以看出用原型链实现继承时,要是有多个对象对同一个属性进行修改,而且这个属性还是引用类型,容易造成全部对象的那个属性都被迫修改了,即子类实例共享父类引用属性。还不可以向父类传参。
2、借用构造函数
<script> function Parent(){ this.arr = [11]; this.fun = function (){ //... } } function Children(){ Parent.call(this);//核心 } var child1 = new Children(); var child2 = new Children(); console.log(child1.arr);//[11] console.log(child2.arr);//[11] //只修改child2 child2.arr.push(66); console.log(child1.arr);//[11] console.log(child2.arr);//[11,66] //函数 console.log(child1.fun() === child2.fun()); //false </script>
可以看出解决了子类实例共享父类引用属性的问题,此方法还可以在 call 中向父类进行传参。但是无法实现函数复用,每个子类实例都持有一个新的fun函数,太多了就会影响性能,内存爆炸。
3、组合继承
<script> function Parent(){ this.arr = [11]; } //在此处声明函数 Parent.prototype.fun = function (){}; function Children(){ Parent.call(this);//核心 } Children.prototype = new Parent(); //核心 var child1 = new Children(); var child2 = new Children(); console.log(child1.fun() === child2.fun()); //true </script>
把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点,通过Super.call(this);继承父类的基本属性和引用属性并保留能传参的优点;通过Sub.prototype = new Super();继承父类函数,实现函数复用。但是子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的,又是内存浪费,
4、使用 ES6 的 class 关键字
<script> class Parent{ constructor(){ this.a = 66; this.b = 'luck'; } method(){ console.log('我是父类的方法'); } } class child extends Parent{ constructor() { super(); } method() { super.method(); } } const per = new child(); console.log(per) console.log(per.method()); </script>