面向对象的继承方法总共有7种,前6种是ES5实现继承的方法,最后1种是ES6实现继承的方法。1.ES5继承的方法(有六种)1.混入式继承(也叫拷贝继承) 需求: 使用混入式实现继承(了解) 实现原理:将父类成员拷贝到子对象中(浅拷贝), 拷贝(复制)!!! 使用for in循环 将父类复制到子类中。 实现方法:for…in…循环遍历父类,子类[key]=父类[key] 缺点:共享数据安全问题,修改子类,会影响父类,引用数据类型浅拷贝,会修改引用地址,数据安全问题,会造成一改全改,原因在于大家引用的是同一个内存区域中的数据。<script> //假设 obj1父类 obj2子类 var obj1={ name:"张三",//基本类型 age:20, wife:{//引用类型 name:"美丽", age:18 } } var obj2={} // 混入式继承也叫拷贝继承-------语法还是重点 for(var k in obj1){ obj2[k]=obj1[k] } obj2.name="李四" obj2.wife.name="王五" console.log(obj1) console.log(obj2) </script>2.原型式继承 目标: 实现原型式继承(了解) 实现原理: 子类的原型对象指向父类的原型对象,将父类中的原型成员添加到子类的原型链中。 实现方式:子类.prototype = 父类.prototype 弊端: 数据安全,子类会修改父类原型 原型链结构混乱,数据共享安全,只能继承父类原型对象中成员,不能继父类实例对象成员 需求: 子类的实例对象 使用 父类方法: <script> // 父类 function Person(name,age){ this.name=name this.age=age } Person.prototype.eat=function(){ console.log(this.name+"爱吃鱼") } // 子类 function Student(name,age,score){ this.name=name this.age=age this.score=score } // 实现继承 Student.prototype=Person.prototype Student.prototype.getScore=function(){ console.log(this.score) } var s=new Student("张三",19,100) console.log(s) s.eat() s.getScore() </script>3.原型链继承 目的: 实现 原型链继承(掌握) 实现原理:将子类的原型对象指向父类的实例对象。 1. 实现原型链继承 子类原型指向父类实例 Student.prototype = new Person(); // 不需要传参 2. 注意点: 记得将constructor属性指向子类 Student.prototype.constructor = Student; 3. 子类需要方法,记得加上 Student.prototype.方法= function () { } 实现方法:子类.prototype = new 父类() 存在问题:存在数据共享问题,无法给父类构造函数传递参数,只能继承原型成员,不能继承实例成员,父类的实例成员中属性和子类中的实例成员有相同的属性,但是子类不能使用父类的实例成员。造成代码有重复,属性多次 需求: 子类的实例对象 使用 父类方法<script> // 父类 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log(this.name + "爱吃木桶猪脚饭"); } // 子类 function Student(name, age, score) { this.name = name; this.age = age; this.score = score; } // 1. ***实现原型链继承 子类原型指向父类实例 Student.prototype = new Person(); // 不需要传参 // 2. ***注意点: 记得将constructor属性指向子类 Student.prototype.constructor = Student; // 3. 子类需要方法,记得加上 Student.prototype.getScore = function () { console.log(this.name + "考试" + this.score + "分"); } // 创建子类的实例 var s = new Student("张三",20,130); var p=new Person("李四",12) console.log(s); console.log(p); s.eat(); s.getScore(); console.log(s.constructor); </script>4.借用构造函数继承 目的: 实现 借用构造函数继承 思路: 代码重复-----函数封装 弊端: 只能继承实例成员,不能继承原型成员 实现原理:在子构造函数中调用父构造函数,达到继承并向父构造函数传参的目的,父类构造*函数* 在子类中运行。 实现方法:(改变this指向)将父对象的构造函数设置为子对象的成员,即将父类构造函数赋值给子类的实例对象调用这个方法,类似于将父构造函数中的代码复制到子构造函数中来执行,即子类实例去调用这个方法---父类内部的this指向子类的实例用完之后删掉 这种继承方式都存在下面两个问题:如果父子构造函数存在相同的成员,那么子构造函数会覆盖父构造函数中的成员不能继承原型链中的成员<script> // 需求: 子类继承父类的实例成员 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log(this.name + "爱吃鱼"); } function Student(name,age,score){ // 方法一:函数之间调用,造成this指向window的问题,window中有name,age,score属性 // Person(name,age)//造成this指向window的问题,window中有name,age,score属性 // 方法二:重点:改变this指向 // 思路:1.先将父类构造函数赋值给子类的实例对象,2.子类实例去调用这个方法---父类内部的this指向子类的实例。3.使用完就删除 // 1.先将父类构造函数赋值给子类的实例对象 this.fn=Person // 2.子类实例去调用这个方法---父类内部的this指向子类的实例 this.fn(name,age) this.score=score // 3.用完之后就删除 delete this.fn } var s=new Student("张三",10,100) // s.eat()//报错,s.eat is not a function,因为借用构造函数继承只能继承实例成员不能继承原型成员 console.log(s) </script> 高级实现方法:凡是要借用方法,首先想到使用call或apply <script> // 需求: 子类继承父类的实例成员 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log(this.name + "爱吃鱼"); } function Student(name,age,score){ //方法三:使用call或apply方法 //Person.call(this,name,age) Person.apply(this,[name,age]) this.score=score } var s=new Student("张三",10,100) // s.eat()//报错,s.eat is not a function,因为借用构造函数继承只能继承实例成员不能继承原型成员 console.log(s) </script>5.组合继承 实现原理:基原型链继承(实现原型成员继承)+借用构造函数继承(实现实例成员继承) 目的: 实现 组合继承 缺点:子类的原型对象上有无用属性,子类的原型对象中有无效数据 <script> //父类 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log(this.name + "爱吃盖浇螺蛳面"); } // 子类 function Student(name,age,score){ // 借用构造函数---call和apply更容易实现---------------实现实例成员的继承 Person.apply(this,[name,age]) this.score=score } // 原型链继承----------实现原型成员继承 Student.prototype=new Person() Student.prototype.constructor=Student Student.prototype.getScore=function(){ console.log(this.score) } var s=new Student("张三",20,100) console.log(s) s.eat(); </script>6.寄生组合继承 实现原理:寄生式继承 + 借用构造函数(call apply)。即组合继承的基础上,改造原本的原型链继承。子类和父类中间创建一个空类,过滤掉无用的父类实例属性。 寄生式继承--------对原型链继承的优化 思路: 创造一个中间类,过滤无效数据script> function Person(name,age){ this.name=name this.age=age } Person.prototype.eat=function(){ console.log(this.name+"爱吃鱼") } function Student(name,age,score){ // 借用构造函数---call,apply更容易实现 Person.apply(this,[name,age]) this.score=score } //使用delete删除Super函数或者用立即执行函数删除Super函数 (function(){ // 立即执行函数:目的是只执行一次 Super 用完之后就会删除 // 函数中的变量在函数执行完毕之后 就会销毁 // 函数局部变量的生命周期-------从创建到销毁的过程 var Super=function(){} Super.prototype=Person.prototype//对象a=对象b,然后销毁对象b,对象a不会受影响 Student.prototype=new Super() Student.prototype.constructor=Student })() Student.prototype.getScore=function(){ console.log(this.score) } var s=new Student("张三",20,100) console.log(s) s.eat() </script> 总结: ES5实现继承的方式不止一种。这是因为ES5 中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。2.ES6继承的方法(一种)7.class的继承 继承语法:(ES6中class用extends 和 super实现继承) class 子类 extends 父类{// 类似原型链继承 constructor(name,age,score){ super(name,age);// 类似借用构造函数继承 } } js原则上没有类的概念,底层还是原型链,类的概念只是一个语法糖 类-----类型-----构造函数------构造器 语法: // 需求: 使用 ES6的class类 创造一个 类 ,然后实例化一个对象 class 类名{ // 构造函数 constructor------这里面是实例成员 constructor(){} // constructor同级的函数-----原型成员 方法(){ } // 静态成员 static 方法(){} } 需求:使用ES6的class实现继承 class实现继承的细节:Person中定义了人都应该有的属性和方法使用extends关键字实现Student类继承Person类的功能,此时他们两就属于继承关系了。在Student的构造方法中,使用super关键字调用父类中的构造方法。<script> // 父类 class Person{ constructor (name,age){ this.name=name this.age=age } eat(){ console.log(this.name+"爱吃鱼") } } // 子类 class Student extends Person{ constructor(name,age,score){ super(name,age) this.score=score } getScore(){ console.log(this.score) } } var s=new Student("张三",20,100) console.log(s) s.eat() </script>