JS面向對象,搞懂原型面向對象,es6的class

ECMA-262把面向對象定義爲:「無序屬性的集合,其屬性能夠包含基本值、對象、或者函數」java

熟悉java等其餘面向對象的語言的同窗確定對面向對象的概念很熟悉,經過面向對象,可我能夠把很複雜的邏輯抽象化,提升代碼的複用性提升內聚性,下降耦合度。 而和其餘正統的面向對象不一樣的是,js採用的是原型對象程序員


原型

既然是面向原型的面向對象,咱們首先就須要搞懂原型是什麼。 直接上圖。 es6

原型也就是圖中的 prototype這個屬性了。咱們來看看如何來新建js的對象。

  1. 建立一個新函數,哈數內自動回建立一個prototype屬性。這屬性是一個指針,指向了原型對象
    面試

  2. 在這個原型對象中有一個默認constructor屬性,這個屬性也是一個指針,默認指向它的構造函數,也就是Person方法。(實現繼承的關鍵屬性)
    設計模式

  3. 原型對象中定義咱們須要的相關屬性,如圖上的name,age等。
    bash

  4. 經過new方法新建對象。
    函數

這樣一個js對象就建立完畢了。從這個步驟中咱們也能夠看出來,prototype是js建立對象的核心,理解prototype也是理解js面向對象的第一步。
ui

固然,prototype的內容不止是以上內容,這裏再也不贅述,咱們能夠自行了解。this


構造函數模式

如今最流行的面向對象設計模式叫組合使用構造函數和原型模式,因此咱們須要先理解構造函數模式
直接上代碼:es5

// 構造函數模式
    function Person (name,age){
        this.name = name;
        this.age = age;
        this.sayAge = function (){
            alert(this.age);
        }
    }

    let person = new Person('tjh',11);
    person.sayAge(); //11
複製代碼

學過java的同窗必定知道構造函數是什麼東西。構造函數模式就和構造函數同樣,能夠傳入參數來給對象賦初始值。
優勢:能夠傳入值來給對象付初始值。

缺點:每一個方法也須要給每一個對象建立一次,佔用了多餘的空間,代碼冗餘
好比上述的sayAge方法,咱們每次new一個對象,每一個對象內都存在一個sayAge方法,其實它只須要建立一遍便可。


原型模式

原型模式也是經常使用的構建模式,和構造函數是互補的模式,二者各有所長。
直接上代碼。

function Person() {
    }
    Person.prototype.name = "tjh";
    Person.prototype.age = 11;
    Person.prototype.sayAge = function(){
        alert(this.age)
    }


    let person = new Person();
    person.sayAge();  //11
複製代碼

經過prototype中的constructor指針,咱們把prototype對象從新指向了Person。

優勢:全部對象共享屬性和方法,十分節省內存

缺點
一、沒法傳遞參數,全部實例默認相同

二、當屬性值存在引用類型時,會發生全部實例共用一個引用對象的現象。
緣由是js中,值類型都是按值傳遞的,而引用類型傳遞的是指針!


組合使用構造函數模式和原型模式

理解了上面兩中類型以後,咱們能夠發現,他們的優缺點是互補的。那咱們爲何不把他們結合起來呢?
先看代碼:

//組合使用原型模式和構造函數模式
    function Person(name, age, job) {   //每一個成員的私有變量放在這裏
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ['a','b'];
    }
    Person.prototype = {  //每一個成員的共有方法放在這裏
        constructor: Person,
        sayName: function (){
            alert(this.name);
        }
    }

    let person1 = new Person('tjh',18,'student');
    let person2 = new Person('zxy',13,'wife');
    person1.friends.push('c');
    alert(person1.friends);  //a,b,c
    alert(person2.friends);  //a,b
    person1.sayName();  //tjh
    person2.sayName();  //zxy

複製代碼

咱們把二者的優勢都結合起來,就能夠幾乎完美的模擬類了。簡單來講,只須要把私有的變量或方法放在構造函數中,把共有的成員變量或方法放在prototype中,既保證了代碼重用,又保證了構造函數的不一樣。


ES6的class

ES6的class本質上其實就是es5的面向對象的語法糖, 爲了簡化面向對象的代碼和簡化程序員理解js面向對象的難度。
因此只要咱們理解了es5的面向對象,es6的class只是單單變化了一些語法,是它更靠近正統的面向對象了而已。 class的基本語法很簡單,咱們直接看代碼

// ES6class
    class Person {
        constructor(name,age){
            this.name = name;
            this.age = age;
        }
        sayAge(){
            alert(this.age);
        }
    }

    let person = new Person('tjh',11);
    person.sayAge(); //11
複製代碼

是否是發現了class和組合使用方式用法基本一致!只是簡化了部分代碼。每一個class都有一個constructor函數,若沒有,則會默認返回this。原理基本與組合方式一致。
可是有一點須要注意:class不存在變量提高!


常考面試題

1.prototype(原型)是什麼,它是怎麼使用的?

答:每一個函數都有一個prototype屬性, 它是一個引用變量, 默認指向一個空Object對象,也就是prototype對象 。
當調用一個對象的函數或者屬性的時候,若是在當前對象裏面找不到,那麼就到原型裏面去找,重複以上過程。

2.構造函數模式和原型模式的優缺點各自是什麼?

以在上文解答。

3.寫出下面題目的顯示

function Foo(){
        getName = function(){console.log(1);};
        return this;
    }
    Foo.getName = function(){console.log(2);};
    Foo.prototype.getName = function(){console.log(3);};
    var getName = function(){console.log(4);};
    function getName(){console.log(5);}

    Foo.getName();//2
    getName();//4
    Foo().getName();//1
    getName();//1
    new Foo.getName();//2
    new Foo().getName();//3
    new new Foo().getName();//3

複製代碼

4.實現停車場
參考自 www.jianshu.com/p/02f7f2779…
某停車場,分3層,每層100個車位
每一個車位都能監控到車輛的駛入和離開
車輛進入前,顯示每層的空餘車位數量
車輛進入時,攝像頭可識別車牌號和時間
車輛出來時,出口顯示器顯示車牌號和停車時長

// 停車場
class Park {
    constructor() {
        this.parkNumList = [];    // 每層空餘車位
    }
    getPrakNumList() {
        for (let i of this.parkNumList) {
            console.log(`第${i.floor}層停車場,剩餘停車位${i.parkNum}`);
        }
    }
    registerFloor(floor) {
        this.parkNumList.push(floor);
    }
}

// 層數
class Floor {
    constructor(floor, num) {
        this.floor = floor;       // 層數
        this.parkNum = num;       // 該層剩餘車空位
    }
    carEnter() {
        this.parkNum = this.parkNum - 1;
    }
    carLeave() {
        this.parkNum = this.parkNum + 1;
    }
}

// 停車位
class ParkSpace {
    constructor(num, belongFloor) {
        this.parkSpaceNum = num;           //停車位編號
        this.belongFloor = belongFloor;    //停車位屬於哪一層  
        this.isAir = false;                //停車位是否空閒
    }
    monitorCarEnter() {
        this.isAir = false;
        this.belongFloor.carEnter();
        console.log('-------停車位已監控到車輛進入------');
    }
    monitorCarLeave() {
        this.isAir = true;
        this.belongFloor.carLeave();
        console.log('-----停車位已監控到車輛離開-----');
    }
}

// 車
class Car {
    constructor(carNum) {
        this.carNum = carNum;        // 車牌號
        this.carEnterTime = '';      // 車進入停車位的時間
        this.carLeaveTime = '';      // 車離開停車位的時間
    }
    beforeEnter(park) {
        park.getPrakNumList();
    }
    enter(parkSpace, camera, enterTime) {
        this.carEnterTime = enterTime;
        parkSpace.monitorCarEnter();
        camera.shot(this);
    }
    leave(parkSpace, monitor, leaveTime) {
        this.carLeaveTime = leaveTime;
        parkSpace.monitorCarLeave();
        monitor.show(this);
    }
}

// 攝像頭
class Camera {
    shot(car) {
        console.log(`攝像頭已識別車牌號:${car.carNum}`);
        console.log(`攝像頭已識別車輛進入時間:${car.carEnterTime}`);
    }
}

// 出口顯示器
class Monitor {
    show(car) {
        console.log(`出口顯示器顯示車牌號:${car.carNum}`);
        console.log(`出口顯示器顯示車輛停車時長:${car.carEnterTime} - ${car.carLeaveTime}`);
    }
}

// 舒店長要開停車場營業啦~
let park = new Park();                     //初始化停車場
let firstFloor = new Floor(1, 100);        //初始化一樓停車場
park.registerFloor(firstFloor);
let secondFloor = new Floor(2, 100);       //初始化二樓停車場
park.registerFloor(secondFloor);
let thirdFloor = new Floor(3, 100);        //初始化三樓停車場
park.registerFloor(thirdFloor);
let camera = new Camera();                 //初始化攝像頭
let monitor = new Monitor();               //初始化出口顯示屏


// 歡迎第一輛車停車~
let car1 = new Car('桂G8888');
car1.beforeEnter(park);
let parkSpace1 = new ParkSpace(88, secondFloor);     //這個車想停在二樓停車場的88號位置
car1.enter(parkSpace1, camera, '12:45:30');

// 而後又來了第二輛車~
let car2 = new Car('贛B8888');
car2.beforeEnter(park);
let parkSpace2 = new ParkSpace(88, thirdFloor);     //這個車想停在三樓停車場的88號位置
car2.enter(parkSpace2, camera, '23:01:30');

// 而後又來了第三輛車~
let car3 = new Car('粵A6666');
car3.beforeEnter(park);
let parkSpace3 = new ParkSpace(88, thirdFloor);     //這個車想停在一樓停車場的88號位置
car3.enter(parkSpace3, camera, '19:01:30');

// 而後又來了第四輛車~可是沒有停車~唉
let car4 = new Car('粵A6666');
car4.beforeEnter(park);
複製代碼

結語:搞懂js面向對象只是js的基本入門,你我還需繼續努力。

相關文章
相關標籤/搜索