JavaScript 高级篇-02 (原型链/ES6类与继承)

1.面向对象三大特征介绍

  • ​ 1.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现(前面学习的api其实就是一种封装思想)
  • 2.继承:一个对象拥有其他对象的属性和方法
  • 3.多态:一个对象在不同情况下的多种状态

2.继承的三种实现方式

2.1混入式继承 : 遍历 父对象 的所有属性值,添加给 子对象

  • 特点:每继承一次,就要执行一次循环
  • 应用场景:单个对象继承
    在这里插入图片描述
//继承:让一个对象拥有另一个对象的属性和方法
        let father = {
            house: {
                address: '北京一环',
                price: 100000000
            },
            car: {
                brand: '劳斯莱斯',
                price: 5000000
            }
        }
        let son = {
            name: 'ikun',
            age: 30
        }

        //1.混入式
        //解决方案:遍历父对象的所有属性值,添加给子对象
        //特点:每继承一次,就要执行一次循环
        //应用场景 : 单个对象继承

        for (let key in father) {
           son[key] = father[key]
        }
        console.log(son)

2.2替换原型继承 : 将 父对象 作为 子对象构造函数的原型

  • 特点:会丢失原型之前的成员变量
  • 应用场景:多个对象继承
    在这里插入图片描述
let father = {
            house: {
                address: '北京一环',
                price: 100000000
            },
            car: {
                brand: '劳斯莱斯',
                price: 5000000
            }
        }

        function Son(name, age) {
            this.name = name
            this.age = age
        }
        Son.prototype.sayHi = function () {
            console.log('你好')
        }
        
        /* 替换原型继承: 把父对象作为子对象构造函数的原型
           特点:用于多对象继承,但是会覆盖原来的原型
       */
        Son.prototype = father

        console.log(Son.prototype);

        //实例化对象
        let son1 = new Son('ikun', 30)
        console.log(son1)
        let son2 = new Son('班长', 20)
        console.log(son2)

2.3混合式继承 : 混入式 + 替换原型

  • 特点 : 遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型
        //父对象
       let father = {
           house : {
               address:'武汉天地',
               price : 10000000
           },
           car:{
               brand:'劳斯莱斯幻影',
               price:8000000
           }
       }

       //子对象构造函数
       function Son(name,age){
        this.name = name
        this.age = age
       }

       //默认原型
       Son.prototype.eat = function(){
           console.log('吃东西')     
       }
       /* 混合式 : 混入式 + 替换原型
            * 遍历父对象成员, 添加给子对象构造函数原型
            * 特点: 多对象继承,但是不会覆盖默认原型
       */
       for(let key in father){
           Son.prototype[key] = father[key]
       }

       let son1 = new Son('爽哥',20)
       let son2 = new Son('ikun',30)
       console.log(son1,son2)

3.(重点)原型链(框架底层基础)

3.1原型链 :

  • 每一个实例对象都有自己的原型,而原型也是对象,也有自己的原型。以此类推,形成链式结构,称之为原型链。

3.2原型链访问规则 :就近原则

  • 对象优先访问自己的成员,自己没有就找原型的,如果原型也没有,就找原型的原型。以此类推,直到原型链终点null. 如果还找不到,属性则获取undefined,方法则报错undefined is not a function
  • 原型对象里的成员 实例对象可以直接使用 因为实例对象里面有_proto_属性指向原型对象

3.3原型链作用 : 继承

  • js语言 通过 原型链 实现面向对象继承

只有对象才有原型,这里一定要把基本数据类型string、number、boolean,和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
在这里插入图片描述

<script>
        /* 
        1.原型链 :每一个对象,都有__proto__指向自身的原型。 而原型也是对象,也有自己的
        __proto__指向原型的原型,以此类推形成链式结构,称之为原型链。

        2.对象访问原型链中成员的规则 :就近原则
            当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问,
            没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 : null.
            还没有, 如果是属性 : 则获取undefined  如果是方法 ,则报错xxx is not defined 
         */
        
        //1.构造函数
        function Person(name,age){
            this.name = name;
            this.age = age;
        };

        //2.原型对象
        Person.prototype.sayHi = function(){
            console.log('人生若只如初见,何事秋风悲画扇');
        };

        Person.prototype.type = '哺乳动物';

        //3.实例化对象
        let p1 = new Person('又又',18);
        console.log(p1);

        //请说出以下代码执行结果
        console.log(p1.name);//又又      p1自己有name属性
        console.log(p1.type);//哺乳动物   p1自己没有type,但是p1的原型有
        console.log(p1.hobby);//undefined p1自己没有hobby,原型也没有
        p1.sayHi();// 人生若只如初见,何事秋风悲画扇   p1自己没有这个方法,原型有
       // p1.eat();//报错 xxx is not defined    p1自己没有这个方法,原型也没有

       //为什么不报错?  p1自己没有这个方法,原型也没有这个方法,但是原型的原型有
        p1.toString();

        //查看p1的原型链
        console.log(p1.__proto__.constructor);//Person
        console.log(p1.__proto__ === Person.prototype);//true

        //查看p1的原型的原型
        console.log(p1.__proto__.__proto__.constructor);//Object
        console.log(p1.__proto__.__proto__ === Object.prototype);//true

        //查看p1的原型的原型的原型
        console.log(p1.__proto__.__proto__.__proto__);//null

    </script>

4.Array的原型链

在这里插入图片描述

//1.Array
    let arr = new Array(10,20,30);
    console.log ( arr );
    //查看arr的原型
    console.log ( arr.__proto__.constructor );//Array
    console.log ( arr.__proto__ === Array.prototype );
    //查看arr的原型的原型
    console.log ( arr.__proto__.__proto__.constructor );//Object
    console.log ( arr.__proto__.__proto__ === Object.prototype );//true

5.Date的原型链

在这里插入图片描述

//2.Date
    let date1 = new Date();
    //细节:日期对象直接console.log会转成string,查看对象需要使用console.dir打印
    console.dir(date1);
    console.log ( date1.__proto__ === Date.prototype );//true
    console.log ( date1.__proto__.__proto__.constructor );//Object
    console.log ( date1.__proto__.__proto__ === Object.prototype );//true

6.String对象原型链

在这里插入图片描述

//3.String
    let str = new String('123');
    console.log ( str );
    console.log ( str.__proto__ === String.prototype );//true
    console.log ( str.__proto__.__proto__.constructor );//Object
    console.log ( str.__proto__.__proto__ === Object.prototype );//true

7.DOM对象原型链

在这里插入图片描述

    <div class="box">div盒子</div>
    <p class="pp">p标签</p>
    <script>
        //获取dom对象
        let box = document.querySelector('.box')
        let pp = document.querySelector('.pp')
    </script>

4.instanceof 运算符

  • instanceof关键字(运算符) : 检测 构造函数的原型(prototype属性)是否出现在某个实例对象的原型链中
    在:true
    不在:false
  • instanceof语法: 实例对象 instanceof 构造函数
  • instanceof运算符作用: 限制函数参数数据类型
    例如: … not of type … 报错 函数参数数据类型错误

        //数组实例对象
        // arr->Array.prototype->Object.prototype->null
        let arr = [10,20,30]

        console.log( arr instanceof Array )//true
        console.log( arr instanceof Object )//true
        console.log( arr instanceof String )//false
       //2.示例
    //根据instanceof语法:左边Function表示对象,右边Function表示构造函数
    //Function原型链  Function对象->Function.prototype->Object.prototype->null
    console.log ( Function instanceof Function );//true
    console.log ( Function instanceof Object );//true

    //3.示例
    //根据instanceof语法:左边Object表示对象,右边Object表示构造函数
    //Object原型链 Object对象->Function.prototype->Object.prototype->null
    console.log ( Object instanceof Object );//true
    console.log ( Object instanceof Function );//true     

        //报错: 参数不是节点   
        //instanceof关键字一般用在函数中,用于限制参数的数据类型
        //例如 appendChild()参数只能是node节点,如果你传了其他类型就会报错。底层就会用到instanceof来检测参数数据类型
        let h1 = document.createElement('h1')
        h1.innerText = '我是h1标签'
        document.body.appendChild(h1)

5.ES6类与继承(class/extends/super)

51.class关键字 :

作用: 声明一个类函数 (相当于ES5的构造函数,只是代码的阅读性大大提高)

  • 1.把构造函数和原型全部写在一个大括号里面,提高代码阅读性
  • 2.必须要使用new才可以调用class函数,否则会报错 提高代码规范

class关键字语法:

        class 构造函数名{
                    constructor(){
                     //(1)这里写构造函数代码
                    };
                    //(2)原型里面的方法写在下面
                    eat(){
                    };
                    play(){
                    };
                };       

在这里插入图片描述

class本质其实就是构造函数的另一种写法,底层的原理不变 , 本质还是给prototype添加成员, 使用了class,一样可以继续使用ES5的prototype
在这里插入图片描述

5.2extends关键字

在这里插入图片描述

  • 作用: 类继承

  • 语法: class 子类函数 extends 父类函数{ }

  • 底层: 替换原型对象的原型继承(不会覆盖子构造函数的原型)

    • 替换原型:
      s1.proto = Person.prototype (会覆盖s1原来的原型)
    • extends关键字替换原型的原型:
      s1.proto.proto === Person.prototype (不会覆盖s1原来的原型)
 //ES6类函数
        class Person{
            //1.构造函数
            constructor(name,age){
                this.name = name
                this.age = age
            }

            //2.原型方法
            eat(){
                console.log('吃东西')
            }
        }

        //3.实例对象
        let p1 = new Person('ikun',30)
        console.log( p1 )

        /* extends关键字作用: 类继承 
        语法: class 子类函数 extends 父类函数
        底层: 替换原型继承(不会覆盖子构造函数的原型)
            我们课堂讲的替换原型:  s1.__proto__ = Person.prototype (会覆盖s1原来的原型)
            extends关键字替换原型的原型:
                s1.__proto__.__proto__ === Person.prototype (不会覆盖s1原来的原型)
        */
       class Student extends Person{
            //子构造函数原型方法
            learn(){
                console.log('我今天学的很开心')
            }
       }

       let s1 = new Student('陈爽',20)
       console.log(s1)
       s1.learn()//调用子类自己的方法
       s1.eat()//调用父类方法 (继承:拥有父类所有的成员)
5.3super : 子类中调用父类方法
  • (1)应用 : 如果子类要写自己的constructor,则必须要先调用父类的constructor,否则程序会报错
  • (2)语法: super( )
应用场景: 写在子类的constructor方法中
                 class 子类函数 extends 父类函数{
                    //子类构造函数
                    constructor(name,age,score){
                        super(name,age)
                        this.score = score
                    }
                    //子类原型方法
                    learn(){
                        
                    }
                }
        //父类函数:Person
        class Person{
            //构造函数
            constructor(name,age){
                this.name = name
                this.age = age
            }

            //原型方法
            sayHi(){
                console.log('i love you')  
            }
        }  
    
        //子类函数:teacher
        class Teacher extends Person{
            //构造函数
            constructor(name,age,className){
                //父类方法
                super(name,age)
                this.className = className
            }

            //子类原型
            learn(){
                console.log('一日为师终身为友') 
            }
        }

        let t1 = new Teacher('ikun',30,'71期')
        console.log(t1)//Teacherage: 30className: "71期"name: "ikun"[[Prototype]]: Person
        
    </script>

6.函数补充

6.1 函数中的关键字: arguments

  • 作用:arguments:获取函数所有的实参(伪数组)
  • 场景: 例如 arr.push() 底层就是用arguments获取实参
       function fn(a,b){
        console.log(a,b)//10,20
        console.log( arguments )//获取函数所有实参
       }

       fn(10,20,30,40,50)

6.2.rest参数(剩余参数) : …形参名

  • 作用:获取所有的剩余参数(真数组)
  • 场景: 绝大多数情况下,剩余参数可以替代arguments
 function fn(...b){
        console.log(b)//获取所有的剩余参数
        console.log( arguments )//获取函数所有实参
       }

       fn(10,20,30,40,50)

6.33.函数默认参数:

ES5 : 逻辑 或 中断
ES6 : 形参 = 默认值

  • 1.函数传参本质是:实参给形参赋值
  • 2.函数形参和实参数量可以不一致,但是会按照顺序一一赋值
 function fn(a=10,b=20){
            //找真 : 左边为真,返回左边的值。反之返回右边的值
            // a = a || 10
            // b = b || 20
            console.log(a)//5
            console.log(b)//20
        }    
        fn(5)//底层  fn(5,undefined)

7.总结:

7.1.面向对象三大特征

  • a.封装:将某个功能封装到对象或函数中
  • b.继承:一个对象拥有另一个对象的所有成员变量(属性和方法)
  • c.多态:一个对象在不同情况的多种状态

7.2.实现继承的几种方式

  • a.混入式继承

    • 解决方案:遍历父对象的所有属性值,添加给子对象
    • 弊端:每继承一次,就要执行一次循环
    • 应用场景:父对象只有一个子对象
  • b.替换原型

    • 解决方案:将父对象作为子对象构造函数的原型
    • 弊端:会丢失原型之前的成员变量
    • 应用场景:自定义内置对象
  • c.混合式(混入+替换原型)

    • 解决方案:遍历父对象所有的属性值,添加给构造函数的原型
    • 应用场景:父对象有多个子对象

7.3.原型链

  • 原型链:每一个对象都有原型,原型本身又是对象,所以原型又有原型,以此类推形成一个链式结构,称为原型链
  • 对象在原型链中的访问规则:就近原则
    • 当访问对象成员变量时,会先从自己的属性中查找,如果有就访问,没有就访问自己原型的,如果原型中没有,则访问原型的原型,以此类推,如果访问到原型链的顶端还是没有,则程序报错xxxx is not undefined
    • 特殊情况:Object.prototype的原型对象是null
  • .函数本身也是对象
    • 构造函数属于函数

7.4.instanceof运算符

  • 语法:对象 instanceof 构造函数
  • 作用:检测构造函数的原型prototype在不在这个对象的原型链上

7.5.js语言是通过什么技术实现面向对象继承的

  • 答案:原型链

7.6原型链终点是什么

  • 答案:null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值