javascript高级语法之七 :ES5实现继承(原型) - ES6实现继承 (class)

javascript高级语法之七 :ES5实现继承 - ES6实现继承 

 

 Object是所有类的父类

对象的原型 :javascript每个对象都有一个特殊的内置属性[[prototype]]

  查找原则 : 对象找不到的属性,就去原型里面找

  获取原型 :

    1.对象.__proto__ , 可以获取(但是这个是早期浏览器自己添加的,存在一定的兼容性问题)

    2.Object.getPrototypeOf(对象)方法可以获取到

 

函数的原型 :prototype

  1.将函数看成一个普通的对象,它就有__proto__(隐式原型)

    作用 : 查找key对应的value时,会摘到原型身上

  2.将函数看成一个函数,它是具备prototype(显示原型)     【注意 : 对象是没有prototype的】

    作用 :在通过new操作符,创建对象时,将这个显示原型赋值给创建出来对象的隐式原型

    这里就关系到了new的

    new操作符的作用 : 

      1.创建空对象

      2.将这个空对象的赋值给this

      3.将函数的显示原型赋值给这个对象,作为它的隐式原型

      4.执行函数体中的代码

      5.将这个对象默认返回

 

    <script>
        function Fn(name,age,height){
            this.name = name
            this.age = age
            this.height = height
            // 这样的操作的话,就会创建很多个函数了.性能不好
            // this.eat = function(){
            //     console.log(this.name + 'eating')
            // }
            // this.running = function(){
            //     console.log(this.name + 'running')
            // }
        }

        // 由于上面性能不好.我们要放在函数的prototype身上
        // 因为对象在找规则是从自身找,自身找不到就从proto身上找
        // 又由于 : new的第三个优点 : 函数的显示原始会赋值给new出来这个对象的隐式原型
        // 那么久可以去对象的隐式原型身上找到

        // 
        Fn.prototype.eat = function(){
            console.log(this.name + 'eating')
        }

        Fn.prototype.running = function(){
            console.log(this.name + 'running')
        }

        let one =  new Fn('无语天',18,19.33)
        console.log(one.eat())

        let two =  new Fn('羊志伟',28,29.44)
        console.log(two.eat())

        let three =  new Fn('小黑',38,39.55)
        console.log(three.eat())
        
        console.log(one.eat() === two.eat())  //true
        console.log(Fn.prototype.eat() === two.eat())  //true


        console.log(one,two,three)
    </script>

 1.显示原型对象上门有一个属性 : constructor

   函数的显示原型的constructor 等于这个函数

   例如 : Person.prototype.constructor === Person

 2.实例对象身上也有constructor的。因为p身上的隐式原型 = 函数的显示原型的

   

 

二.面向对象的特性 --继承性

  面向对象有三个特性 : 封装,继承,多态

    封装 : 将对象和方法封装到一个类中,就叫封装

    继承 :继承是面向对象中非常重要的,不仅仅可以减少重复代码的数量,也是多态前提(纯面向对象中)

    多态 :不同的对象在执行时表现出不同的形态

 

  没有继承前的代码 : 

    <script>
        function Fn(name,age,fn){
            // 第一和第二
            this.name = name
            this.age = age
            // 唯一
            this.fn = fn
        }
        // 重复
        Fn.prototype.eat = function(){
            console.log(this.name + 'eating')
        }
        // 重复
        Fn.prototype.running = function(){
            console.log(this.name + 'running')
        }

        // 唯一
        Fn.prototype.fn = function(){
            console.log(this.name + 'fn ')
        }

        // ---------------------------------
        function Teach(name,age,teach){
            this.name = name
            this.age = age
            // 唯一性
            this.teach = teach
        }
        Teach.prototype.eat = function(){
            console.log(this.name + 'eating')
        }
        Teach.prototype.running = function(){
            console.log(this.name + 'running')
        }

        // 唯一性
        Teach.prototype.teach = function(){
            console.log(this.name + 'teach')
        }
       
    </script>

 

  继承可以帮助我们把重复的代码和逻辑抽取到父类中,子类只需要直接继承过来使用即可

  继承也是多态的前提

  es5 : 要实现继承,那么需要学习原型链

  采用原型链实现继承 : [ 后面采用es6实现继承比较好 ]

    1.借用构造函数(实现属性)+原型链(实现方法)来实现继承 ==》 还是不完美,有缺点

      

    2.还有好多种方案(直接写最好的方案)  

    <script>
        // --优化方法的--
        function createObject(o){
            function F(){}
            F.prototype = o
            return new F()
        }
        function inherit(Subtype,Supertype){
            Subtype.prototype = createObject(Supertype.prototype)
            Object.defineProperty(Subtype.prototype,"constructor",{
                enumerable:false,
                configurable:true,
                writable:true,
                value:Subtype
            })
        }
        //--优化方法的--

        function Person(name,age,height){
            this.name = name
            this.age = age
            this.height = height
        }
        Person.prototype.running = function(){
            console.log('running')
        }
        Person.prototype.eating = function(){
            console.log('eating')
        }

        function Student(name,age,height,sno,score){
            Person.call(this,name,age,height)
            this.sno = sno
            this.score = score
        }
        inherit(Student,Person) 
        Student.prototype.studying = function(){
            console.log('studying')
        }
        
        // 创建实例对象
        var stu1 = new Student('yjx',18,1.88,111,100)
        var stu2 = new Student('wyt',38,1.88,111,100)
        console.log(stu1,stu2)

    </script>

 

  

原型链图 :

   

 

ES6的继承(class  extends后面都采用这种!)      

认识class定义类

  按照前面的构造函数形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。

  在ES6使用了class关键字来直接定义类  - - -  但在本质上依然是前面所讲的构造函数,原型链的语法糖而已

    <script>
        class Person{
            // 1.类中的构造函数
            // 当我们通过new关键字调用一个Person类时,默认调用class的constructor方法
            constructor(name,age){
                this.name = name
                this.age = age
            }
            // 2.实例方法
            // 本质上放在Person.prototype
            running(){
                console.log(this.name + 'running')
            }
            eating(){
                console.log(this.name + 'eating')
            }
        }

    </script>

   跟 Object.defineProperty  一样

    <script>
        class Rectangle{
            constructor(x,y,width,height){
                this.x = x
                this.y = y
                this.width = width
                this.height = height
            }
            //有get set 跟 Object.defineProperty 一样的功能
            get position(){
                return {x:this.x,y:this.y}
            }
        
        }
        
        var rec1 = new Rectangle(10,20,100,200)
        console.log(rec1.position)

    </script>

  实例方法 和 类方法 的区别 : 

    <script>
        function Person() {}
        // 实例方法
        Person.prototype.running = function() {}

        // 类方法
        Person.randomPerson = function() {}

        var  p1 = new Person()
        // 实例方法
        p1.running()

        // 类方法
        Person.randomPerson()

        // ---------- ES6的class的类方法和实例方法 -------------
        class Person{
            // 实例方法
            running(){}

            // 类方法(也叫静态方法--需要关键词 static 区分)
            static randomPerson(){}
        }

        var  p1 = new Person()
        // 实例方法
        p1.running()

        // 类方法
        Person.randomPerson()
    </script>

  ES6的继承 

    <script>
        // es6定义class实现的继承
        // 1。父类 
        class Person {
            constructor(name,age){
                this.name = name
                this.age = age 
            }
            running(){
                console.log('running')
            }
        }

        // 子类
        class Student extends Person{
            constructor(name,age,sno,score){
                // 注意 : super必须放在this前面
                // 1.super.method(...)来调用父类的方法
                // 2.super(...)来调用父类的constructor
                super(name,age)
                this.sno = sno 
                this.score = score
            }
            // 如果子类对父类的方法实现不满足,重新实现
            running(){
                console.log('不满足,重新写,然后我还要用父类里面的')
                // 这样后,也会打印出来running
                super.running()
            }
            studying(){
                console.log(studying)
            }
        }

        var s1 = new Student()
        console.log(s1.running())
    </script>

继承的使用---扩展 : 

    <script>
        // 继承内置类,并且做一些扩展
        // 这里我们做一个可以得到数组的最后一个 lastItem
        class HyArray extends Array{
            get lastItem(){
                return  this[this.length-1]
            }
        }

        var arr1 = new HyArray(100,200,30)
        console.log(arr1.lastItem) //得到30
        // 其实我们可以直接这样做
        Array.prototype.lastItem = function() {
            return  his[this.length-1] ;
        }
    </script>

 

  面向对象的--多态

  不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现 【从定义上看,js到处都是多态】

  多态存在的条件 

    1.必须有继承(实现接口)

    2.必须有父类引用指向子类对象 

  

  ES6 对象字面量的增强写法

  

    <script>
        // 属性的简写案例 : 
        var name  = "why"
        var age  = "age"

        var obg = {
            name : name ,
            age : age
        }
        // 简写 : key和value是一样的,就可以省略【语法糖】
        var obg = {
            name ,
            age
        }

        
        function foo(){
            var message = "123"
            var info = "234"
            return {
                message : message,
                info : info 
            }
            // 简写 
            return {message, info }
        }


        // 方法的简写案例
        var obj = {
            funning: function() {

            },
            // 上面的简写
            funning(){

            },

            eating : ()=>{

            }
        }

        
        // 计算属性名
        var key = "addrss"
        var obj = {
            [key] : "广州" //相当于 address :"广州"
        }
    </script>

 

posted @ 2022-07-25 18:42  杨建鑫  阅读(370)  评论(0编辑  收藏  举报