js中继承的几种方式
-
什么是继承?
- 继承是指一个类能够使用另一个类的属性和方法。被继承的那个类称为父类,继承父类属性和方法的那个类叫做子类,即子类能够使用父类里的属性和行为。
-
继承的几种方式
-
一、原型链继承
- 特点:属性是共享的,如果属性中有引用数据类型,那么修改其中一个,另外一个也会修改。
- 实现方式:
在要继承的子类的原型上面赋值为父类的实例化
例子如下:
//父函数 function Parent(name,age) { this.name = name; this.age = age; this.hobby = ["音乐", "电影", "睡觉"] } //原型上面添加方法 run Parent.prototype.run = function () { console.log(this.name + "今年" + this.age); return this.name + "今年" + this.age } //要继承的子函数 function Child() { } // 原型链继承:在Child的原型上赋值为 Parent的实例化 实现继承 // new Child()会生成一个实例化对象 此时这个对象有上面的name age hobby属性 还有原型上面的 run方法 // 并将这个实例化对象 赋值给Child的原型 以实现继承 //这时候再实例化 Child 就可以实现继承了 Child.prototype = new Parent("小王",18); //继承实现的核心代码 let newChildA = new Child(); let newChildB = new Child(); console.log(newChildA,newChildA.name); console.log(newChildB,newChildA.name); console.log(newChildA.run()); // 原型链继承的特点 如果属性有引用类型 继承的是引用的地址 // 改变一个 另外一个也会改变 // 例如:属性hobby为数组 改变newChildA的 打印newChildB的也改变了 newChildA.hobby.push("篮球"); console.log(newChildA,newChildA.hobby); console.log(newChildB,newChildB.hobby); //改变基本类型不受影响 newChildA.name = "小周"; newChildB.name = "小秋"; console.log(newChildA); console.log(newChildB);
-
二、构造函数继承
-
特点:
- 1、可以解决原型链继承中共享属性的情况,不存在引用类型一个修改,另外一个也会发生变化的情况。
- 2、构造函数继承不会继承原型链上面的属性及方法
-
实现方式:
使用this绑定的形式,来实现继承
例子如下://父类 function Parent(name, age) { this.name = name; this.age = age; this.hobby = ["音乐", "电影", "睡觉"] } //原型上面添加方法 run Parent.prototype.run = function () { console.log(this.name + "今年" + this.age); return this.name + "今年" + this.age } //子类 function Child(name,age) { Parent.call(this,name,age); //构造函数的继承,使用this绑定的形式来获得父类含有的属性 } let newChildA = new Child("小何",24); let newChildB = new Child("小何",24); console.log(newChildA); console.log(newChildB); //改变其中一个的引用类型 newChildA.hobby.push("篮球"); console.log(newChildA); // hobby: (4) ['音乐', '电影', '睡觉', '篮球'] console.log(newChildB); // hobby: (4) ['音乐', '电影', '睡觉'] //打印可以看到,相互是不会影响的,解决了原型链继承共享属性的情况 newChildA.run() //会报错,此时实例上面没有 run方法 构造函数继承不会继承原型链上面的方法
-
-
三、组合继承
-
特点:
- 1、相当于把构造函数继承与原型链继承组合在一起
- 2、及可以解决原型链继承共享属性的情况又可以实现继承父类原型链上的属性及方法
-
缺点:
- 会实例化两次,在改变子对象this指向的时候会实例化一次,在赋值父实例的时候也会实例化一次
-
实现方式:
构造函数继承与原型链继承的方式都有
详见下面例子function Parent(name, age) { this.name = name; this.age = age; this.hobby = ["音乐", "电影", "睡觉"] } //原型上面添加方法 run Parent.prototype.run = function () { console.log(this.name + "今年" + this.age); return this.name + "今年" + this.age } function Child(name, age) { Parent.call(this, name, age); //构造函数继承 会实例化一次 } Child.prototype = new Parent() //原型链继承 会再一次实例化 // 以上两个地方会 实例化两次 let newChildA = new Child("小何", 24); let newChildB = new Child("小何", 24); console.log(newChildA); console.log(newChildB); newChildA.run() //run方法调用成功
-
-
四、寄生组合继承
-
实现方式:
调用Object.create方法,并传入父类的原型,再将返回值赋值给子类的原型
function Parent(name, age) { this.name = name; this.age = age; this.hobby = ["音乐", "电影", "睡觉"] } //原型上面添加方法 run Parent.prototype.run = function () { console.log(this.name + "今年" + this.age); return this.name + "今年" + this.age } function Child(name, age) { Parent.call(this, name, age); //构造函数继承 } Child.prototype = Object.create(Parent.prototype) //与组合继承的差异 //通过create方法创造了一个对象,并且这个对象的proto指向Parent的prototype let newChildA = new Child("小何", 24); let newChildB = new Child("小何", 24); console.log(newChildA); console.log(newChildB); newChildA.run() //run方法调用成功
-
-
五、Es6方式实现继承
- 实现方式:子类通过 extends 关键字并在子类的constructor中调用 super() 实现继承
class Parent { constructor() { this.age = 18; } } class Child extends Parent { constructor() { super(); this.name = "张三" } } let o1 = new Child(); console.log(o1, o1.name, o1.age);
-