JS:js面向对象ES5和ES6的继承的方法考点(五颗星)
面向对象的继承方法总共有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>
前端面试的一些常问问题、问题的具体实现(可直接运行)以及底层原理
SHEIN希音公司福利 222人发布