zoukankan      html  css  js  c++  java
  • 第四章-面向对象编程

    1 面向对象

      一般地, 类是对象的类型模板, 实例是根据类创建的对象

      但是在JavaScript中不区分类和实例, 而是通过原型(prototype)来实现面向对象编程

      JavaScript不区分类和实例的情况就是, 实例是一个对象, 我们想要创建的类也是一个对象, 用

    实例对象.__proto__ = 类对象;

      来表明继承, 而且由于是对一个属性进行设定, 因此只能单继承

      使用原型相当于继承

      

      但是如果再给xiaoming绑定一个_proto_, 那原来绑定的东西就没有了(单继承)

      使用__proto__并不是一般的使用方式, 一般的时候方式是编写一个类似于new的方法来创建一个对象

      可以使用

    Object.create(类对象);

      来返回一个基于类对象的空的对象, 通过对该对象的属性进行绑定, 可以完成创建一个机遇类对象的对象(实例), 具体如下

    // 原型对象(类):
    var Student = {
        name: 'Robot',
        height: 1.2,
        run: function () {
            console.log(this.name + ' is running...');
        }
    };
    
    function createStudent(name) {
        // 基于Student原型创建一个新对象:
        var s = Object.create(Student);
        // 初始化新对象:
        s.name = name;
        return s;
    }
    
    var xiaoming = createStudent('小明');
    xiaoming.run(); // 小明 is running...
    xiaoming.__proto__ === Student; // true

    2 创建对象

      对象访问属性的过程:

        在当前对象查找, 没有找到就找它原型对象有没有, 再没有就找object的原型上查找, 如果都没查找到, 就返回undefined

    xiaoming -> xiaoming.prototype -> object.prototype -> null

      constructor

        该属性会获得该对象的prototype的源对象

      关于prototype和constructor的关系

      

      构造函数 

        由于之前都是类是一个对象, 在创建一个函数来生成一个实例

        有没有类似于class类的一步到位既创建了类有可以生成实例的呢, 那就是构造函数了

        构造函数实际上就是一个普通的函数, 函数内容就类似于定义一个类, 在生成对象的时候, 是调用new 函数名来创建

        但是注意的是, 构造函数中的this指的就是新创建的对象, 而且该函数会默认返回this, 所以不要自己单独写return

        由于构造函数与普通函数并没有区别, 因此也可以像普通函数那样直接使用, 但是这样场产生的this会出现异常, 因此为了区分普通函数与构造函数

        一般地:  

          构造函数首字母大写

          普通函数首字母小写

        

      另外, 继承自相同类的不同对象, 得到的方法尽管在功能上是相同的, 但是是不一样的函数, 类似于python中绑定方法

      要使不同的对象共享一个函数, 可以针对父类的prototype来绑定原来放在父类中的函数

    function Student(name) {
        this.name = name;
    }
    
    Student.prototype.hello = function () {
        alert('Hello, ' + this.name + '!');
    };

      在JavaScript中还可以写一个函数来专门封装new, 同时将传入的参数也一并用对象的方式整体传入, 这样就完成了一个封装性更高, 可用性更强的类

    function Student(props) {
        this.name = props.name || '匿名'; // 默认值为'匿名'
        this.grade = props.grade || 1; // 默认值为1
    }
    
    Student.prototype.hello = function () {
        alert('Hello, ' + this.name + '!');
    };
    
    function createStudent(props) {
        return new Student(props || {})
    }
    
    var xiaoming = createStudent({
        name: '小明'
    });
    
    xiaoming.grade;

      这样设置有一个很大的好处, 那就是传参十分方便, 因为实际上定义的props是一个对象数据类型, 绑定好了就不需要顺序对应, 想什么时候取值就什么时候取值

    3 原型继承

      由于JavaScript采用原型继承, 不存在Class这种类型, 因此无法像Java那样使用类的扩展来继承

      要想继承一个类, 需要想办法把原型链修改为

    new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null

      可以参考道格拉斯的方法, 在中间创建一个空的函数来桥接

    // PrimaryStudent构造函数:
    function PrimaryStudent(props) {
        Student.call(this, props);
        this.grade = props.grade || 1;
    }
    
    // 空函数F:
    function F() {
    }
    
    // 把F的原型指向Student.prototype:
    F.prototype = Student.prototype;
    
    // 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
    PrimaryStudent.prototype = new F();
    
    // 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
    PrimaryStudent.prototype.constructor = PrimaryStudent;
    
    // 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
    PrimaryStudent.prototype.getGrade = function () {
        return this.grade;
    };
    
    // 创建xiaoming:
    var xiaoming = new PrimaryStudent({
        name: '小明',
        grade: 2
    });
    xiaoming.name; // '小明'
    xiaoming.grade; // 2
    
    // 验证原型:
    xiaoming.__proto__ === PrimaryStudent.prototype; // true
    xiaoming.__proto__.__proto__ === Student.prototype; // true
    
    // 验证继承关系:
    xiaoming instanceof PrimaryStudent; // true
    xiaoming instanceof Student; // true

      可以简化为

    function inherits(Child, Parent) {
        var F = function () {};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
    }

      复用方式为

    function Student(props) {
        this.name = props.name || 'Unnamed';
    }
    
    Student.prototype.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
    
    function PrimaryStudent(props) {
        Student.call(this, props);
        this.grade = props.grade || 1;
    }
    
    // 实现原型继承链:
    inherits(PrimaryStudent, Student);
    
    // 绑定其他方法到PrimaryStudent原型:
    PrimaryStudent.prototype.getGrade = function () {
        return this.grade;
    };

    4 class继承

      由于原型继承尽管简单, 但是理解起来困难, 因此ES6提供了class继承

      编写类基本格式如下

    class 类名{
        constructor(参数列表){
            构造函数函数体;
        }    
    
        函数名(参数列表){
            一般函数的函数体;
        }    
    }

      具体定义和使用如

    class Student {
        constructor(name) {
            this.name = name;
        }
    
        hello() {
            alert('Hello, ' + this.name + '!');
        }
    }
    
    var xiaoming = new Student('小明');
    xiaoming.hello();

      继承的基本格式如下

    class 子类名 extends 父类名{
        constructor(参数列表){
              super(参数列表);
              子类构造方法体;
        }
        
        方法名(参数列表){
            函数体;    
        }
    
    }    

      具体代码为

    class PrimaryStudent extends Student {
        constructor(name, grade) {
            super(name); // 记得用super调用父类的构造方法!
            this.grade = grade;
        }
    
        myGrade() {
            alert('I am at grade ' + this.grade);
        }
    }

      由于现在不是全面支持ES6, 因此现在还不普及, 但是如果真的要使用的话, 可以使用Babel工具来将写好的class继承转化为原型继承    

  • 相关阅读:
    VB中Null、Empty、Nothing及vbNullString的区别
    hs_err_pidXXX.log 解读
    测试Windows Live Writer——开博
    BCPC2021预赛
    软件设计模式之策略模式(Strategy) 壹
    留言板 壹
    友链 壹
    正则表达式练习 壹
    SpringBoot+Mybatis+自定义注解+Atomikos+实现多源数据库切换和分布式事务
    Dependency failed for File System Check on /dev/vdb1 服务器配置升级
  • 原文地址:https://www.cnblogs.com/weihuchao/p/6912291.html
Copyright © 2011-2022 走看看