zoukankan      html  css  js  c++  java
  • 小结JS中的OOP(上)

    前言:大家都知道,OOP有三大特性:封装,继承,多态。下面是自己对这三个特性的理解:

    封装:把属性与方法整合到某种数据类型中。目的是让类的使用者按类的编写者的意愿去使用类。在封装过程中会一般会做两件事:

    ① 隐藏内部实现 ② 对外提供接口(访问权限控制)。

    继承:共享父类的属性与方法

    多态:不同对象执行相同的操作,可以产生不同的结果。关于多态要注意两点:

    ① 在子类以父类的形式存在时,不能使用子类的属性与方法
    ② 子类在工作时,采用自己的实现方式。

    下面我们以java为例子看看OOP的三个特性。

    /**
     * 定义类:Animal
     */
    public class Animal {
        private String cate;
    
        public String getCate() {
            return cate;
        }
    
        public void setCate(String cate) {
            this.cate = cate;
        }
    
        public void shout() {
            System.out.println("Animal shouted");
        }
    }
    
    /**
     * 定义类:Dog,并让其从Animal继承
     */
    class Dog extends Animal {
        @Override
        public void shout() {
            System.out.println("dog shouted");
        }
    }
    
    /**
     * 定义类:Collie,并让其从Dog类继承
     */
    class Collie extends Dog {
        @Override
        public void shout() {
            System.out.println("collie shouted");
        }
    
        public void graze() {
            System.out.println("collie graze");
        }
    }
     
    //测试代码
    public class OOPTest {
        public static void main(String[] args) {
            //封装
            Animal animal = new Animal();
            animal.shout();//Animal shouted
    
            //继承: Dog中并没有声明cate域,也没有对应的setter/getter
            Dog dog = new Dog();
            dog.setCate("dog");
            System.out.println(dog.getCate());//dog
    
            //多态
            Animal dog2 = new Collie();
            dog2.shout();//collie shouted
            //dog2.graze();error! 在子类以父类的形式存在时,不能使用子类的属性与方法
            ((Collie)dog2).graze();//进行向下转型后可以运行,打印出:collie graze
        }
    }

    一: OOP在JS中的实现

    但对于Js而言,目前还没有实现class关键字,也没有private,protected,public权限修辞符。但我们可以使用闭包来实现封装:

    /**
     * 定义Animal类型
     * @returns {Animal}
     * @constructor
     */
    function Animal() {
        var _cate;
    
        //防止直接调用Animal构造函数在window上新增属性
        if(this instanceof Animal) {
            this.setCate = cateSetter;
            this.getCate = cateGetter;
            this.shout = shout;
        } else {
            return new Animal;
        }
    
        //使用函数名cateSetter方便理解
        function cateSetter(cate) {
            _cate = cate;
        }
    
        function cateGetter() {
            return _cate;
        }
    
        function shout() {
            console.log('Animal shouted');
        }
    
    }
    //使用:
    var animal = new Animal();
    animal.shout();//animal shouted
    //直接不能访问到cate属性,只有通过对外提供的方法(setCate/getCate)去访问
     animal.setCate('animal~~');
     console.log(animal.getCate());//animal~~
     

    JS中的继承是通过prototype来实现的,现在新增Dog类型,并从Animal继承:

    /**
     * 定义Dog类型,并让其从Animal继承
     * @constructor
     */
    function Dog() {}
    Dog.prototype = new Animal();
    //让instanceof运行符可以正常工作
    Dog.prototype.constructor = Dog;
    //测试:
    var dog = new Dog();
    //dog具有了方法shout, cate的getter/setter
    dog.setCate('dog');
    console.log(dog.getCate());//dog
    dog.shout();//animal shouted
    console.log(dog instanceof Dog);//true
    console.log(dog instanceof Animal);//true

    接着定义类型Collie,并实现多态:

    /**
     * 定义Collie类型,并让其从Dog继承
     * @constructor
     */
    function Collie(){}
    //让Collie从Dog继承
    Collie.prototype = new Dog;
    Collie.prototype.constructor = Collie;
    //为Collie新增graze方法
    Collie.prototype.graze = function() {
        console.log('collie graze');
    }
    //重写Collie的shout方法
    Collie.prototype.shout = function() {
        console.log('collie shouted');
    }
    //测试
    var collie = new Collie();
    collie.setCate('collie');
    console.log(collie.getCate());//collie
    console.log(collie instanceof Collie);//true
    console.log(collie instanceof Dog);//true
    console.log(collie instanceof Animal);//true
    //多态:相同的方法,产生了不同的行为
    collie.shout(); //collie shouted
     

    完整的OOP实现代码:

    /**
     * 定义Animal类型
     * @returns {Animal}
     * @constructor
     */
    function Animal() {
        var _cate;
    
        //防止直接调用Animal构造函数
        if(this instanceof Animal) {
            this.setCate = cateSetter;
            this.getCate = cateGetter;
            this.shout = shout;
        } else {
            return new Animal;
        }
    
        //使用函数名cateSetter方便理解
        function cateSetter(cate) {
            _cate = cate;
        }
    
        function cateGetter() {
            return _cate;
        }
    
        function shout() {
            console.log('animal shouted');
        }
    
    }
    
    /**
     * 定义Dog类型,并让其从Animal继承
     * @constructor
     */
    function Dog() {}
    Dog.prototype = new Animal();
    //让instanceof运行符可以正常工作
    Dog.prototype.constructor = Dog;
    
    /**
     * 定义Collie类型,并让其从Dog继承
     * @constructor
     */
    function Collie(){}
    //让Collie从Dog继承
    Collie.prototype = new Dog;
    Collie.prototype.constructor = Collie;
    //为Collie新增graze方法
    Collie.prototype.graze = function() {
        console.log('collie graze');
    }
    //重写Collie的shout方法
    Collie.prototype.shout = function() {
        console.log('collie shouted');
    }

    二: 缺点

    这种构造函数(闭包)+prototype的实现的缺点:

    1. 使用闭包模拟私有属性时,造成同一类型的多个实例共享一个相同闭包变量(Dog.prototype = new Animal() 只生成了一个闭包变量

    2. 每次实例化一个子对象时,都先要实例化一个父对象

    3. 不能在子对象上调用父对象上的同名方法

    4. 引用类型的共享Bug(在prototype上面的引用类型都会有这个问题,因为各个function的prototype是一个指针,实际的prototype对象在堆中只有一份内存分配)

    5. 封装不优雅,很散乱

    缺点1的测试代码:

    var dog = new Dog();
    var dog2 = new Dog();
    //dog1,dog2共享闭包变量_cate,明显这不合适
    dog.setCate('dog1');
    console.log(dog.getCate());//dog1
    dog2.setCate('dog2');
    console.log('+++'+dog.getCate());//dog2
    console.log('+++'+dog2.getCate());//dog2
  • 相关阅读:
    Linux系统网络文件配置
    Linux系统修改日期时间
    8、mysql索引
    7、mysql正则表达式、事务、alter命令
    6、mysql数据操作
    5、mysql数据类型
    4、mysql数据库操作
    3、mysql管理
    2、mysql安装
    1、mysql教程
  • 原文地址:https://www.cnblogs.com/jagusOu/p/3822140.html
Copyright © 2011-2022 走看看