JS物件導向

weixin_34253539發表於2018-07-24

一. JavaScript物件導向前言

1.1 什麼是物件?

Everything is object (萬物皆物件)。
物件到底是什麼,我們可以從兩個層次來理解。

  1. 物件是單個事物的抽象

       一本書、一輛汽車、一個人都可以是物件,一個資料庫、一張網頁、一個與遠端伺服器的連線也可以是物件。當實物被抽象成物件,實物之間的關係就變成了物件之間的關係,從而就可以模擬現實情況,針對物件進行程式設計。
       
    
  2. 物件是一個容器,封裝了屬性(property)和方法(method)

       屬性是物件的狀態,方法是物件的行為(完成某種任務)。比如,我們可以把動物抽象為animal物件,使用“屬性”記錄具體是那一種動物,使用“方法”表示動物的某種行為(奔跑、捕獵、休息等等)
       在實際開發中,物件是一個抽象的概念,可以將其簡單理解為 : 資料集或功能集。
    

1.2 什麼是物件導向?

物件導向只是過程式程式碼的一種高度封裝,目的在於提高程式碼的開發效率和可維護性

小編的理解:物件導向就是說,使用物件的時候,你可以直接使用它所提供的功能而忽略其內部組成情況。面對物件不一定只有在程式設計界裡才有,我們生活中無處不在;比如說,你家裡的電視機,你使用了遙控,就能操作電視機,但是你實際上不知道這臺電視機裡面是什麼零件組成的,你只要知道,我拿到遙控就可以操作電視機就好了。這就是一種物件導向的思想。

1.2.1 什麼是物件導向程式設計?

物件導向程式設計 —— Object Oriented Programming,簡稱 OOP ,是一種程式設計開發思想。
它將真實世界各種複雜的關係,抽象為一個個物件,然後由物件之間的分工與合作,完成對真實世界的模擬。
在物件導向程式開發思想中,每一個物件都是功能中心,具有明確分工,可以完成接受資訊、處理資料、發出資訊等任務。
因此,物件導向程式設計具有靈活、程式碼可複用、高度模組化等特點,容易維護和開發,比起由一系列函式或指令組成的傳統的程式式程式設計(procedural programming),更適合多人合作的大型軟體專案。

1.2.2 物件導向與程式導向

  • 程式導向就是親力親為,事無鉅細,面面俱到,步步緊跟,有條不紊
  • 物件導向就是找一個物件,指揮得結果
  • 物件導向將執行者轉變成指揮者
  • 物件導向不是程式導向的替代,而是程式導向的封裝

二. 物件定義的兩種方式

2.1 字面量的方式進行定義

        var  obj = {
                    name: "Tom ",
                    sex: " man",
                    age:19,
                    run:function(){
                        console.log("一天跑一公里");
                    }
          }

2.2 使用 new Object() 進行定義

         var   obj = new  Object();
         obj.name = "Tom ";
         obj.sex = " man";
         obj.age = 19;
         obj.run = function(){
                     console.log("一天跑一公里");
         }

三. 類和物件

3.1 什麼是類(Class)?

具有相同特性(資料元素)和行為(功能)的物件的抽象就是類。因此,物件的抽象是類,類的具體化就是物件,也可以說類的例項是物件,類實際上就是一種資料型別。類具有屬性,它是物件狀態的抽象,用資料結構來描述類的屬性。類具有操作,它是物件行為的抽象,用操作名和實現該操作的方法來描述。

3.2 類和物件的區別

作為初學者,容易混淆類和物件的概念。類(Class)是一個抽象的概念,物件則是類的具體例項。比如:人是一個類,司馬遷,李白,杜甫都是物件;首都是一個類,則北京,倫敦,華盛頓,莫斯科都是物件;動物貓是一個類,則Kitty、Grafield 和 Doraemon 都是物件。
我們可以說 Kitty 貓的體重是 1.5kg,而不能說貓類的體重是 1.5kg;可以說李白是詩人,而不能說人類是詩人。狀態是描述具體物件而非描述類的,行為是由具體物件發出的而非類發出的。

3.3 類和物件的關係

類與物件的關係就如模具和鑄件的關係,類例項化的結果就是物件,而物件的抽象就是類,類描述了一組有相同特性(屬性)和相同行為的物件。

    class person{ }//這個是類
    $obj = new person();//類的例項化就是物件

四.建立物件的三種方式

4.1 工廠模式,使用簡單的函式建立物件,為物件新增屬性和方法,然後返回物件

 // Class 模板
    function  Person(name,sex,age){
        var  obj = {};
        obj.name = name;
        obj.sex = sex;
        obj.age = age;
        obj.run = function(){
            console.log("每天堅持跑步");
        }
        return obj;
    }
    // 例項化
    var  person1 = Person("Tom","sex",19);
    //操作
    person1.run();  //  輸出結果:每天堅持跑步

4.1.1 工廠模式的優缺點

優點:
1、 在工廠模式中,使用者只需要知道所要生產的具體東西,無須關心具體的建立過程,甚至不需要具體產品類的類名。
2、 在系統增加新的產品時,我們只需要新增一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了“開閉原則”。
缺點:
1、 每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

4.2 建構函式模式。建立自定義引用型別,可以像建立內建物件例項一樣使用new操作符,這種方法的缺點是,建構函式的每個成員都無法複用,每次建立出的物件都只有私有變數和私有方法,不能實現共用

//建構函式(這裡就建立了一個Class模板)
    function Person(name,sex,age){
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.run = function(){
            console.log("每天堅持跑步");
        }
    }
 // 例項化 ( new Object())
    var person1 = new Person("Tom","man",19);
 //操作
    person1.run();// 每天堅持跑步

4.2.1 建構函式的改進

    // 構造全域性的物件
    var  action = {
        run:function(){
            console.log("每天堅持跑步");
        }
    }
    //建構函式
    funcction(name,sex,age){
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.run = action.run;
    }
    //例項化
    var person1 = new Person("Tom","man",19);
    person1.run();

分析: 為什麼要改進建構函式?
我們都知道當例項化一個物件後,那麼這個物件就擁有了模板的屬性和方法,
當我們使用方法時首先會在記憶體中開闢一份空間,然後在執行相應的操作。假設我們例項化一萬個物件,那麼這一萬個物件在執行方法時都要在記憶體中開闢空間,這樣只會浪費記憶體的空間,這時如果能用一個操作代替一萬個操作那樣豈不是美哉,所以小編這裡用了一個全域性的物件,即使是一萬個物件使用這個方法只會在記憶體中開闢一份空間。
但是這也有缺點,我們都知道全域性變數和區域性變數,全域性變數易造成汙染並且宣告週期比較長,那麼全域性物件也是一樣的,有沒有一種方式可以不用全部物件呢?當然有,見下面的第三種建立模式

4.3 原型模式,使用建構函式的prototype屬性來指定共享的屬性和方法,即使用建構函式定義例項屬性,使用原型定義共享的屬性和方法

//建構函式
    function Person(name,sex,age){
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    //使用原型物件   Object.prototype
    Person.prototype.run = function() {
        console.log("每天堅持跑步");
    }
    //例項化
    var person1 = new  Person("Tom","man",19);
    person1.run();//  每天堅持跑步

五.建構函式、原型物件、例項化物件三則的關係

首先先明白幾個屬性:
__proto__: 物件的原型。所有物件都有(包括函式)

prototype:函式的原型物件。只有函式(準確來說是建構函式)才有

constructor:物件上的一個屬性,預設指向這個原型的建構函式
圖片描述

六.徹底理解js中this的指向

首先必須要說的是,this的指向在函式定義的時候是確定不了的,只有函式執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個呼叫它的物件(這句話有些問題,後面會解釋為什麼會有問題,雖然網上大部分的文章都是這樣說的,雖然在很多情況下那樣去理解不會出什麼問題,但是實際上那樣理解是不準確的,所以在你理解this的時候會有種琢磨不透的感覺),那麼接下來我會深入的探討這個問題。

eg1:

      function  show(){
            var user = "Tom";
            console.log(this.user);//undefined
            console.log(this);//window
        }
        show();
按照我們上面說的this最終指向的是呼叫它的物件,這裡的函式show()實際是被Window物件所呼叫出來的,下面的程式碼就可以證明。
     function  show(){
            var user = "Tom";
            console.log(this.user);//undefined
            console.log(this);//window
        }
        window.show();
和上面程式碼結果一樣,所以此時this是window呼叫出來的

eg2:

           var show = {
            user:"Tom",
            fn:function(){
                console.log(this.user);//Tom
            }
        }
        show.fn();
這裡的this指向的是物件show,因為你呼叫這個fn是通過show.fn()執行的,那自然指向就是物件show,這裡再次強調一點,this的指向在函式建立的時候是決定不了的,在呼叫的時候才能決定,誰呼叫的就指向誰,一定要搞清楚這個。

要想徹底搞懂this的指向請看下面的例子

eg3:

          var show = {
            user:"Tom",
            fn:function(){
                console.log(this.user);//Tom
            }
        }
        window.show.fn();
  這段程式碼和上面的那段程式碼幾乎是一樣的,但是這裡的this為什麼不是指向window,如果按照上面的理論,最終this指向的是呼叫它的物件,這裡先說個而外話,window是js中的全域性物件,我們建立的變數實際上是給window新增屬性,所以這裡可以用window點show物件。

這裡先不解釋為什麼上面的那段程式碼this為什麼沒有指向window,我們再來看一段程式碼

     var show = {
            a:20,
            b:{
                a:22,
                fn:function(){
                    console.log(this.a);// 22
                }
            }
        }
        show.b.fn();

  這裡同樣也是物件show點出來的,但是同樣this並沒有執行它,那你肯定會說我一開始說的那些不就都是錯誤的嗎?其實也不是,只是一開始說的不準確,接下來我將補充一句話,我相信你就可以徹底的理解this的指向的問題。

  情況1:如果一個函式中有this,但是它沒有被上一級的物件所呼叫,那麼this指向的就是window,這裡需要說明的是在js的嚴格版中this指向的不是window,但是我們這裡不探討嚴格版的問題,你想了解可以自行上網查詢。

  情況2:如果一個函式中有this,這個函式有被上一級的物件所呼叫,那麼this指向的就是上一級的物件。

  情況3:如果一個函式中有this,這個函式中包含多個物件,儘管這個函式是被最外層的物件所呼叫,this指向的也只是它上一級的物件,例子3可以證明,如果不相信,那麼接下來我們繼續看幾個例子。

       var show = {
           a:20,
           b:{
               fn:function(){
                   console.log(this.a);// undefined
               }
           }
       }
       show.b.fn();

儘管物件b中沒有屬性a,這個this指向的也是物件b,因為this只會指向它的上一級物件,不管這個物件中有沒有this要的東西。

還有一種比較特殊的情況,eg4:

    var show = {
           a:20,
           b:{
               a:22,
               fn:function(){
                   console.log(this.a);// undefined
                   console.log(this);//window
               }
           }
       }
       var d = show.b.fn;
       d();

這裡this指向的是window,是不是有些蒙了?其實是因為你沒有理解一句話,這句話同樣至關重要。

 this永遠指向的是最後呼叫它的物件,也就是看它執行的時候是誰呼叫的,例子4中雖然函式fn是被物件b所引用,但是在將fn賦值給變數d的時候並沒有執行所以最終指向的是window,這和例子3是不一樣的,例子3是直接執行了fn。

this講來講去其實就是那麼一回事,只不過在不同的情況下指向的會有些不同,上面的總結每個地方都有些小錯誤,也不能說是錯誤,而是在不同環境下情況就會有不同,所以我也沒有辦法一次解釋清楚,只能你慢慢地的去體會。

建構函式版this:

          function Fn(){
                this.user = "Tom";
            }
        var a = new Fn();
        console.log(a.user); //Tom

這裡之所以物件a可以點出函式Fn裡面的user是因為new關鍵字可以改變this的指向,將這個this指向物件a,為什麼我說a是物件,因為用了new關鍵字就是建立一個物件例項,理解這句話可以想想我們的例子3,我們這裡用變數a建立了一個Fn的例項(相當於複製了一份Fn到物件a裡面),此時僅僅只是建立,並沒有執行,而呼叫這個函式Fn的是物件a,那麼this指向的自然是物件a,那麼為什麼物件a中會有user,因為你已經複製了一份Fn函式到物件a中,用了new關鍵字就等同於複製了一份。

當this碰到return:

     function Fn(){
            this.user = "Tom";
            return {};
        }
        var a = new Fn();
        console.log(a.user);//undefined

繼續:

     function Fn(){
            this.user = "Tom";
            return  funciton(){};
        }
        var a = new Fn();
        console.log(a.user);//undefined

goon:

     function Fn(){
            this.user = "Tom";
            return  1;
        }
        var a = new Fn();
        console.log(a.user);// Tom

再繼續:

     function Fn(){
            this.user = "Tom";
            return undefined;
        }
        var a = new Fn();
        console.log(a.user);//Tom
如果返回值是一個物件,那麼this指向的就是那個返回的物件,如果返回值不是一個物件那麼this還是指向函式的例項
            function Fn(){
            this.user = "Tom";
            return null;
        }
        var a = new Fn();
        console.log(a.user);//Tom
雖然null也是物件,但是在這裡this還是指向那個函式的例項,因為null比較特殊

七.總結

這是今天學的看的所有內容,希望對正在學習前端的朋友有所幫助,有什麼問題可以留言,我們一起進步!

相關文章