小結JS中的OOP(上)

前言:你們都知道,OOP有三大特性:封裝,繼承,多態。下面是本身對這三個特性的理解:java

封裝:把屬性與方法整合到某種數據類型中。目的是讓類的使用者按類的編寫者的意願去使用類。在封裝過程當中會通常會作兩件事:閉包

① 隱藏內部實現 ② 對外提供接口(訪問權限控制)。ide

繼承:共享父類的屬性與方法函數

多態:不一樣對象執行相同的操做,能夠產生不一樣的結果。關於多態要注意兩點:測試

① 在子類以父類的形式存在時,不能使用子類的屬性與方法
② 子類在工做時,採用本身的實現方式。
this

下面咱們以java爲例子看看OOP的三個特性。spa

/**
 * 定義類: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中的實現prototype

但對於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繼承:code

/**
 * 定義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
相關文章
相關標籤/搜索