一般使用字面量的形式直接創建對象,但是這種創建方式對于創建大量相似對象的時候,會產生大量的重復代碼。但 js和一般的面向對象的語言不同,在 ES6 之前它沒有類的概念。但是可以使用函數來進行模擬,從而產生出可復用的對象創建方式,常見的有以下幾種:
(1)第一種是工廠模式,工廠模式的主要工作原理是用函數來封裝創建對象的細節,從而通過調用函數來達到復用的目的。但是它有一個很大的問題就是創建出來的對象無法和某個類型聯系起來,它只是簡單的封裝了復用代碼,而沒有建立起對象和類型間的關系。
(2)第二種是構造函數模式。js 中每一個函數都可以作為構造函數,只要一個函數是通過 new 來調用的,那么就可以把它稱為構造函數。執行構造函數首先會創建一個對象,然后將對象的原型指向構造函數的 prototype 屬性,然后將執行上下文中的 this 指向這個對象,最后再執行整個函數,如果返回值不是對象,則返回新建的對象。因為 this 的值指向了新建的對象,因此可以使用 this 給對象賦值。構造函數模式相對于工廠模式的優點是,所創建的對象和構造函數建立起了聯系,因此可以通過原型來識別對象的類型。但是構造函數存在一個缺點就是,造成了不必要的函數對象的創建,因為在 js 中函數也是一個對象,因此如果對象屬性中如果包含函數的話,那么每次都會新建一個函數對象,浪費了不必要的內存空間,因為函數是所有的實例都可以通用的。
(3)第三種模式是原型模式,因為每一個函數都有一個 prototype 屬性,這個屬性是一個對象,它包含了通過構造函數創建的所有實例都能共享的屬性和方法。因此可以使用原型對象來添加公用屬性和方法,從而實現代碼的復用。這種方式相對于構造函數模式來說,解決了函數對象的復用問題。但是這種模式也存在一些問題,一個是沒有辦法通過傳入參數來初始化值,另一個是如果存在一個引用類型如 Array 這樣的值,那么所有的實例將共享一個對象,一個實例對引用類型值的改變會影響所有的實例。
(4)第四種模式是組合使用構造函數模式和原型模式,這是創建自定義類型的最常見方式。因為構造函數模式和原型模式分開使用都存在一些問題,因此可以組合使用這兩種模式,通過構造函數來初始化對象的屬性,通過原型對象來實現函數方法的復用。這種方法很好的解決了兩種模式單獨使用時的缺點,但是有一點不足的就是,因為使用了兩種不同的模式,所以對于代碼的封裝性不夠好。
(5)第五種模式是動態原型模式,這一種模式將原型方法賦值的創建過程移動到了構造函數的內部,通過對屬性是否存在的判斷,可以實現僅在第一次調用函數時對原型對象賦值一次的效果。這一種方式很好地對上面的混合模式進行了封裝。
(6)第六種模式是寄生構造函數模式,這一種模式和工廠模式的實現基本相同,我對這個模式的理解是,它主要是基于一個已有的類型,在實例化時對實例化的對象進行擴展。這樣既不用修改原來的構造函數,也達到了擴展對象的目的。它的一個缺點和工廠模式一樣,無法實現對象的識別。