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指向)

  1. 将父对象的构造函数设置为子对象的成员,即将父类构造函数赋值给子类的实例对象
  2. 调用这个方法,类似于将父构造函数中的代码复制到子构造函数中来执行,即子类实例去调用这个方法---父类内部的this指向子类的实例
  3. 用完之后删掉

这种继承方式都存在下面两个问题:

  1. 如果父子构造函数存在相同的成员,那么子构造函数会覆盖父构造函数中的成员
  2. 不能继承原型链中的成员
<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实现继承的细节:

  1. Person中定义了人都应该有的属性和方法
  2. 使用extends关键字实现Student类继承Person类的功能,此时他们两就属于继承关系了。
  3. 在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>

前端面试题 文章被收录于专栏

前端面试的一些常问问题、问题的具体实现(可直接运行)以及底层原理

全部评论

相关推荐

希望被捞的猫头鹰很理智:大概率待遇低怕硕士跑路
点赞 评论 收藏
分享
01-23 19:12
门头沟学院 Java
榨出爱国基因:你还差 0.1% 就拿到校招礼盒,快叫朋友给你砍一刀吧
投递拼多多集团-PDD等公司10个岗位
点赞 评论 收藏
分享
评论
1
4
分享

创作者周榜

更多
牛客网
牛客企业服务