小结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
posted @ 2014-07-03 12:41  jagus720  阅读(156)  评论(0编辑  收藏  举报