# Ch 11: Prototype pattern & Revealing Module pattern (opinionted) ###### tags: `clean code` ## Prototype 模式 > ### **`Object.create()`, `Object.assign()`** :::info 不像 constructor function 或是 class,先建立好一個 "原型",再由這個"原型"去做出 object 實體,Prototype pattern 是直接用一個既有 object 作為"樣板",做出以此 object 為"樣板"的其他 object ::: ### Example ```javascript= const myCar = { name: "Ford Escort", drive: function () { console.log(`Weeee. I'm driving ${this.name}!!!`); }, panic: function () { console.log("Wait. How do you stop this thing?"); } }; const yourCar = Object.create(myCar); yourCar.name = "Honda CRV" yourCar.drive(); // Weeee. I'm driving Honda CRV!!! ``` <img src="https://i.imgur.com/jpZ1ELb.png" width=450px /> <br> :::warning 實際上,這邊的 Prototype 模式可能更接近於: - **物件擴展模式 Object extension pattern** - 無建構函式的原型繼承 No-Constructor-Approach to prototypal inheritance :heavy_exclamation_mark: 這是一種罕見的模式,經典 OOP 模式 (早期的 constructor function 或 ES6 後的 class) 通常較受青睞 ~~(於是我擅自決定忽略 p.348 的 code )~~ ::: <br> --- ### Usage 何時使用 :::success - 數個 object 之間具有一些相同性質,但又有不同"特徵" - 運用 **`Object.create()`** 或 **`Object.assign`** 的"擴展機制" ,讓物件間經由繼承來"相互關聯" ::: #### Example 麥當勞黑牛堡系列 ```javascript= const 黑牛堡 = { officialName: "經典安格斯黑牛堡", protien: "安格斯黑牛", veg: "番茄", sauce: "美乃滋", bread: "比較好吃的麵包" }; // using const 蕈菇黑牛堡 = Object.create(黑牛堡); 蕈菇黑牛堡.officialName = "蕈菇安格斯黑牛堡"; 蕈菇黑牛堡.veg = "蕈菇"; const 第戎黑牛堡 = Object.assign({}, 黑牛堡, { officialName: "法式第戎黑牛堡", sauce: "第戎醬" }); ``` <img src="https://i.imgur.com/65mZAg9.png" width=550px /> ===== <br> 降低成本,將身為"樣板" 的黑牛堡,改成使用一般麵包 ```javascript= 黑牛堡.bread = "一般麵包" console.log(蕈菇黑牛堡.bread); // 比較好吃的麵包 ``` :::danger 其它 object 被使用樣板做完以後,就與樣板沒有連結了,不會同步更動共用 property ::: 如果我們希望他們仍然有連結 ```javascript= const relatedObject = Object.assign(Object.create(templateObject), otherExtendProperties) ``` i.e.改寫上方,全部黑牛堡系列都同步 ```javascript= const 黑牛堡 = { officialName: "經典安格斯黑牛堡", protien: "安格斯黑牛", veg: "番茄", sauce: "黃芥末醬", bread: "比較好吃的麵包" } const 蕈菇黑牛堡 = Object.assign(Object.create(黑牛堡), { officialName: "蕈菇安格斯黑牛堡", veg: "蕈菇" }); 黑牛堡.bread = "一般麵包" console.log(蕈菇黑牛堡.bread); // 一般麵包 ``` <br> --- ## Revealing Module 模式 > #### 透過 **IIFE** 與閉包,回傳 public 的 methods 和 properties :::info - 被用來 **『封裝』** 一些**私有**邏輯 - 僅 **『開放』** 一組**公用**的 API ::: ```javascript= const myRevealingModule = function () { let privateName = "Dong"; let publicGreet = "Hey there!"; function privateFunction() { console.log( "Name:" + privateName ); } function publicSetName( strName ) { privateName = strName; } function publicGetName() { privateFunction(); } // Reveal public pointers to private functions and properties return { setName: publicSetName, greeting: publicGreet, getName: publicGetName }; }(); myRevealingModule.getName(); // Name:Dong myRevealingModule.setName( "WYT" ); myRevealingModule.getName(); // Name:WYT ``` ### Usage 何時使用 :::success - 需要在**私有**與**公共**之間劃清界線時 - 具有特定的初始化邏輯時 ::: ### But... :::warning - 在 **class, `#`, `private`** 存在之前,Revealing Module 模式是 "模擬" 真實 **『私有』** 的唯一簡便方法,也因此,現在它有點失寵了 : ) - 現在使用它的,通常是出於個人美學偏好 :::