js面向对象

698 阅读5分钟

js面向对象

  万事万物皆对象,面向对象的主要是把事物给对象化,包括其属性和行为(方法)。易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护


封装

  封装的过程就是把一些属性和方法放到对象中“包裹”起来,即如何创建对象。 假如我们将人(Preson)当作一个对象,它有一个属性(name),一个方法(sayName)

1. 字面量方式创建对象
        let personA = {
            name:"张三",
            sayName:function(){
                console.log(this.name)
            }
        }
        let personB = {
            name:"李四",
            sayName:function(){
                console.log(this.name)
            }
        }
2. 使用new Object()创建对象
        let personA = new Object()
        personA.name = "张三"
        personA.sayName = function () {
            console.log(this.name)
        }
        
        let personB = new Object()
        personB.name = "李四"
        personB.sayName = function () {
            console.log(this.name)
        }

这两种方式实例的对象存在缺点,一是需要实例多个对象时产生大量重复代码,二是创建的对象没有联系,不能反映出是同一个对象的实例

3. 工厂模式创建对象

在介绍工厂模式前,先引入一个类的概念对象的抽象是类,类的具象化就是对象。比如:人就可以看作是一个类,而具体的某一个人就是一个对象,如张三就是一个对象。工厂模式就是把创建对象,添加属性方法,最终返回一个对象放到一个函数中,使用者只需要调用函数无需关注对象创建的细节

        function createPerson(name) {
            let person = new Object();
            person.name = name;
            person.sayName = function () {
                console.log(this.name)
            };
            return person;
        }
        var personA = createPerson("张三")
        var personB = createPerson("李四")
  • 优点:工厂模式解决了字面量方式,new Object()方式大量重复代码问题
  • 缺点:1.工厂模式模式没有解决对象识别问题得到的对象类型都是Object;
       2.工厂模式下,类有新增的属性和方法每次都要生成一个新的类和新的对象
4. 构造函数模式创建对象

构造函数与其他函数的区别就是构造函数需要new关键字来调用。通过new创建的实例一个经历了4步: 1.创建一个新对象; 2.将构造函数内的this绑定到新对象上; 3.给新对象添加属性和方法; 4.返回新对象

        function Person(name) {
            this.name = name;
            this.sayName = function () {
                console.log(this.name)
            };
        }
        var personA = new Person("张三")
        var personB = new Person("李四")
        console.log(personA.constructor)    //Person
        console.log(personA.sayName==personB.sayName) //false
  • 优点:通过构造函数创建的对象会有constructor属性,解决了对象识别问题
  • 缺点:每个实例上的方法都会创建一次存储在不同内存中,浪费了内存
5. 原型模式创建对象

在js中构造函数都存在prototype属性,指向原型对象,这个对象下的属性和方法都会被实例继承。实例对象中会有proto属性,也指向这个原型对象。

        function Person() {}
        Person.prototype.name = "张三"
        Person.prototype.sayName = function () {
            console.log(this.name)
        }
        var personA = new Person()
        var personB = new Person()
        personB.name = "李四"
        personB.sayName() //李四
  • 优点:实例对象中的方法不会多次创建,解决了内存问题
  • 缺点:原型上的属性是共有属性,只能单独设置覆盖原型上的同名属性
6. 原型模式+构造函数模式创建对象

从上面两种方式可以看出来将两种方式结合起来,这样实例对象就能有自己的独立属性,同时又能共享公用属性方法节省内存

        function Person(name) {
            this.name=name
        }
        Person.prototype.sayName = function () {
            console.log(this.name)
        }
        var personA = new Person("张三")
        var personB = new Person("李四")
7. ES6 class类创建对象

es6提出了Class的概念,这使得创建对象的写法更接近面向对象语言的写法。通过class定义对象,默认具有constructor方法和自定义方法

        class Person {
            constructor(name) {
                this.name = name
            }
            sayName() {  //  等同于Person.prototype.sayName
                console.log(this.name)
            }
        }
        var personA = new Person("张三")
        var personB = new Person("李四")
  • 类必须有constructor方法
  • 与es5一样,所有实例共享同一原型对象
  • 类内部定义的方法是不可枚举的

继承

  继承就是子类具有父类公有的属性和方法 假如我们将人(Preson)当作一个对象,它有一个属性(name),一个方法(sayName),然后他有一个子类工人(Worker),子类有两个属性(name、time),两个方法(sayName、sayWorkTime)

1. 构造函数继承

通过call和apply方法将父类构造函数的this绑到子类上

        function Person(name) {
            this.name = name;
            this.sayName = function () {
                console.log(this.name)
            };
        }
        function Worker(name,time){
            Person.call(this,name)
            this.time = time
            this.sayWorkTime = function () {
                console.log(this.time)
            };
        }
        var WorkerA = new Worker("张三","8h")
        console.log(WorkerA.name)    //张三
        console.log(WorkerA.time)    //8h
        WorkerA.sayName() //张三
        WorkerA.sayWorkTime() //8h
  • 缺点:1.子类不能访问父类原型上定义的方法(父类.peototype.方法名);2.每个子类实例上继承父类的方法都会创建一次存储在不同内存中,浪费了内存
2. 原型继承

将父类的实例作为子类的原型

        function Person(name) {
            this.name = name;
            this.info = {
                age:20
            }
            this.sayName = function () {
                console.log(this.name)
            };
        }
        function Worker(time) {
            this.time = time
            this.sayWorkTime = function () {
                console.log(this.time)
            };
        }
        Worker.prototype = new Person()
        var WorkerA = new Worker("8h")
        WorkerA.name = "张三"
        WorkerA.info.age = 26
        var WorkerB = new Worker("12h")
        WorkerB.name = "李四"
        console.log(WorkerA.name,WorkerB.name)    //张三 李四
        console.log(WorkerA.time,WorkerB.time)    //8h 12h
        console.log(WorkerA.info==WorkerB.info)    //true
  • 缺点:1.子类实例不能给父类构造函数传参;2.子类共享父类的引用类型属性(示例中info对象完成相等)
3. 混合继承

混合集成就是综合原型继承和构造函数继承的优点,通过构造函数继承父类实例属性、原型继承父类原型上的属性和方法

        function Person(name) {
            this.name = name;
            this.info = {
                age: 20
            }
        }
        Person.prototype.sayName = function () {
            console.log(this.name)
        };
        function Worker(name, time) {
            Person.call(this, name)//继承父类属性
            this.time = time
        }
        Worker.prototype = new Person()//继承父类方法
        Worker.prototype.sayWorkTime = function () {
            console.log(this.time)
        };
        var WorkerA = new Worker("张三", "8h")
        var WorkerB = new Worker("李四", "12h")
        console.log(WorkerA.name, WorkerB.name)    //张三 李四
        console.log(WorkerA.time, WorkerB.time)    //8h 12h
        console.log(WorkerA.info == WorkerB.info)    //false
        WorkerA.sayName()   //张三
        WorkerA.sayWorkTime()    //8h
4. ES6 class继承

ES6 中class类可以通过extends和super实现继承,子类必须在 constructor 方法中调用 super 方法,否则子类就得不到自己的this对象

        class Person {
            constructor(name) {
                this.name = name
            }
            sayName() {  //  等同于Person.prototype.sayName
                console.log(this.name)
            }
        }
        class Worker extends Person {
            constructor(name, time) {
                super(name)
                this.time = time
            }
            sayWorkTime() {
                console.log(this.time)
            };
        }
        var WorkerA = new Worker("张三", "8h")
        var WorkerB = new Worker("李四", "12h")
        console.log(WorkerA.name, WorkerB.name)    //张三 李四
        console.log(WorkerA.time, WorkerB.time)    //8h 12h
        WorkerA.sayName()   //张三
        WorkerA.sayWorkTime()    //8h

多态

  多态指一个方法多种表现形式.
  如:分别有一个WorkerA和WorkerB对象,分别调用他们的sayname方法,却得到了不同的结果,这就是一个多态.

        class Person {
            constructor(name) {
                this.name = name
            }
            sayName(name) { 
                console.log(name)
            }
        }
        class Worker extends Person {
            constructor(name) {
                super(name)
            }
        }
        var WorkerA = new Worker("张三")
        var WorkerB = new Worker("李四")
        WorkerA.sayName("张三")   //张三
        WorkerB.sayName("李四")   //李四