JS“物件”破冰之旅(二)

靈感愛學習發表於2018-09-29

上一篇文,我們簡要介紹了一下“物件”,比較全面,但比較淺顯,如果只是抱著它,並做不了什麼事,也寫不出足夠好的程式碼,所以我們需要繼續深入~

建立物件

前面我們提到建立物件的兩種最直觀、最簡單的方法,即使用new操作符和物件字面量,但試想一下,按照之前的方法,我們需要建立兩個類似的物件,應該怎麼寫?

//person1
var person1 = new Object();
person1.name = "lilei";
person1.age = 18;
person1.job = “teacher”;
person1.sayName = function(){
    alert(this.name);
}

//person2
var person2 = new Object();
person2.name = "hanmeimie";
person2.age = 20;
person2.job = “doctor”;
person2.sayName = function(){
    alert(this.name);
}
複製程式碼

這裡我們建立了兩個物件,顯然,它們有著相同的屬性和方法,我們重複書寫了兩遍,如果不是兩個,而是多個,那麼我們就要重複N遍,不說JS,在任何一門程式語言當中,這種操作都是會被認為是糟糕的,會盡量避免,為解決這個問題,人們開始探索不同的方法和技巧。

工廠模式

工廠模式,是軟體設計領域廣為人知的一種模式,為什麼稱為“工廠”,也就是說,我們可以像工廠裡生產零件一樣,不需要為每個產品單獨做一個機器,而是某一類產品可以共用一套機器進行批量生產,所以,它可以用來解決建立多個類似物件的問題,例如:

function createPerson(name,age,job){
    var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function(){
            alert(this.name);
        }
        return o;
}

var person1 = createPerson("xiaoli",28,"teacher");
var person2 = createPerson("xiaoliu",29,"doctor");
複製程式碼

上面這段程式碼,使用了一個函式來封裝建立物件的細節,能夠接收引數來建立一個包含所有必要資訊的物件,所以,當需要建立多個類似物件的時候,就不需要把所有東西都再寫一遍造成不必要的程式碼冗餘。

但是,它還有一個缺陷,無法知道一個物件的型別。

建構函式

人們一直在探索和追求更好的編碼方式,有了工廠模式,仍然不滿足,於是又有了許多其他的模式,建構函式便是其一。前面的程式碼也可以這樣寫:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}
var person1 = new Person("xiaoli",28,"teacher");
var person2 = new Person("xiaoliu",29,"doctor");
複製程式碼

乍一看,二者十分相似,但又有著諸多細微差別:

  • 沒有顯示定義物件
  • 屬性賦值給了this
  • 沒有return語句
  • 建立新例項必須使用new操作符

還有另一個小的點,就是建構函式按慣例以大寫字母開頭,以區別於普通函式(畢竟建構函式也是函式)。 由此以來,這種方式建立物件,就要經歷下面四個過程:

  • 建立新物件
  • 函式的作用域給新物件
  • 執行建構函式的程式碼
  • 新物件誕生

這個物件,它既是Object的例項(所有物件均繼承自Object),同時也是Person的例項,自定義的建構函式意味著將來可以將它的例項標識為一種特定的型別,這正是工廠模式所缺少的

上面已經提到,建構函式本身也是函式,所以,你既可以拿來使用new操作符來建立物件,也可以當做一般的函式直接呼叫之。前者已經介紹完,但後者又將被如何處理?

你應該記得,上一篇文章,我們介紹過一種物件,叫做“單體內建物件”,當你無法找到一個屬性或者方法的歸屬時,就會屬於它,建構函式便是如此,比如,像這樣:

Person("xiaoli",28,"teacher"); 
window.sayName();//"xiaoli"
複製程式碼

當它被作為普通函式呼叫的時候,其屬性和方法都會被新增到window物件上去。

問題又來了,既然建構函式模式是工廠的改良版,那它還有改進空間嗎?好像有。

我們說,即使是類似的物件,不同的例項之間屬性是會不同的,像姓名、年齡,但方法很可能是相同的,既然方法相同,為什麼要在每個物件裡面都重新建立一遍?

這個時候,我們會想到一種方法——封裝,將方法封裝起來,為每個例項共享所共享,就像這樣:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName(){
    alert(this.name);
}
複製程式碼

這個做法,看起來十分聰明,但是,可能有點耍小聰明的嫌疑,它依然存在著某些問題:

  • 我們建立了一個全域性函式,可它明明只是提供給Person的例項用的,何來全域性可言?
  • 再者,如果物件需要多個方法,我們把所有方法都定義在建構函式外部,零散地存在著,就背離我們封裝物件的本意了,這不是我們想要的最終答案。

還有哪些改良或者適合不同場景的方法呢,倒也真不少,比如很常見也很重要的“原型模式”,鑑於文章篇幅已經夠長,這篇就到此吧,下一篇我們再倆詳細研究之。

一起加油!回見!

相關文章