js類與繼承與物件關聯風格解讀
阿新 • • 發佈:2020-08-24
前言
讀完了《你不知的js上卷》後,使用了類與繼承與,物件關聯風格與行為委託實現一個簡單的個人任務管理系統。通過實戰後對這兩種設計模式有了更加深刻的瞭解。
程式碼部分對比
類與繼承設計模式
//bulletBox.js (function(_) { var template = `<div class="overlay"> <div class="box"> <div class="box-title"> <h3>訊息提醒</h3> <div class="box-close icon-cross1"></div> </div> <div class="box-main"> <div class="new-title"> <h3 class="nameValue">名稱:</h3> <input type="text"> </div> <button class="btn2 confirm">確定</button> <button class="btn2 cancel">取消</button> </div> </div> </div>`; function Modal(options) { this.options = options || {}; this.container = this.layout.cloneNode(true); this.body = this.container.querySelector('.new-title'); this.bodyContent = this.container.querySelector('.new-title h3'); this.input = this.body.querySelector('input'); // 將options複製到元件例項上 _.mixin(this,options); this._renderUI(); this._initEvent(); return this; } //這裡使用顯示混入的方式,也可以使用原型物件關聯的方式 Modal.prototype = Object.create({}) _.mixin(Modal.prototype, { layout: _.htmlTranslate(template), // 新增節點 appendTo: function(node) { node.appendChild(this.container); }, // 顯示彈窗 show: function(content) { this.container.style.display = 'block'; }, // 隱藏彈窗 hide: function() { this.container.style.display = 'none'; }, // 銷燬彈窗 destroy: function() { this.container.parentNode.removeChild(this.container); }, _renderUI: function() { if(this.hasFlag === true) { // this.body.removeChild('hasInput') this.input.parentNode.removeChild(this.input); this.text && (this.bodyContent.innerText = this.text); } }, _initEvent: function() { _.addEvent(this.container.querySelector('.confirm'), 'click', this.onConfirm.bind(this)); _.addEvent(this.container.querySelector('.cancel'), 'click', this.onCancel.bind(this)); }, onConfirm: function() { this.emit('confirm'); this.destroy(); }, onCancel: function() { this.emit('cancel'); this.destroy(); } }) // 使用混入Mixin的方式使得Modal具有事件發射器功能 _.mixin(Modal.prototype, _.emitter); // 暴露到全域性 window.Modal = Modal; })(util) //main.js new Modal() .on('confirm', function() { if(this.input.value.trim() != '') { var index = ''; if(selectCG.className.indexOf('sub-category') != -1) { index = '' + (data.category.push({name: this.input.value, number: 0, todos: [], subCategory: []}) - 1); } else { index = selectCG.index + '-' + (data.category[selectCG.index].subCategory.push({name: this.input.value, number: 0, todos:[]}) - 1); } } // 更新分類列表 updateCGList(data.category, index); _.save(data); }) .appendTo(document.body);
主要在建構函式function Modal(options) 宣告屬性賦值以及初始化工作,通過混入在Modal.prototype定義方法,然後呼叫new Modal時侯一次實現構造和初始化工作。然後通過鏈式呼叫需要的方法。
物件關聯風格與行為委託設計模式
(function(_) { var template = `<div class="overlay"> <div class="box"> <div class="box-title"> <h3>訊息提醒</h3> <div class="box-close icon-cross1"></div> </div> <div class="box-main"> <div class="new-title"> <h3 class="nameValue">名稱:</h3> <input type="text"> </div> <button class="btn2 confirm">確定</button> <button class="btn2 cancel">取消</button> </div> </div> </div>`; var Modal = { init: function(options) { this.options = options || {}; this.container = this.layout.cloneNode(true); this.body = this.container.querySelector('.new-title'); this.bodyContent = this.container.querySelector('.new-title h3'); this.input = this.body.querySelector('input'); 修改原型鏈使Modal物件的原型鏈上有_.emitter的方法 Object.setPrototypeOf(Modal,_.emitter); this._renderUI(); this._initEvent(); }, layout: _.htmlTranslate(template), // 新增節點 appendTo: function(node) { node.appendChild(this.container); }, // 顯示彈窗 show(content) { this.container.style.display = 'block'; }, // 隱藏彈窗 hide() { this.container.style.display = 'none'; }, // 銷燬彈窗 destroy() { this.container.parentNode.removeChild(this.container); }, _renderUI: function() { if(this.options.hasFlag === true) { // this.body.removeChild('hasInput') this.input.parentNode.removeChild(this.input); this.options.text && (this.bodyContent.innerText = this.options.text); } }, _initEvent: function() { _.addEvent(this.container.querySelector('.confirm'), 'click', this.onConfirm.bind(this)); _.addEvent(this.container.querySelector('.cancel'), 'click', this.onCancel.bind(this)); }, onConfirm() { this.emit('confirm'); this.destroy(); }, onCancel() { this.emit('cancel'); this.destroy(); } } // 暴露到全域性 window.Modal = Modal; })(util)
物件關聯這種風格只需要在元件上定義一個物件,不在需要prototype定義方法屬性了。也不需要顯示混入的複製方式,而是使用es6的新方法 Object.setPrototypeOf()修改[[prototype]]鏈達到同樣的目的。但是在呼叫的時候實現構造例項化初始化的時候需要分兩步(關聯物件和初始化),而且它不能通過類與繼承的方式.().().()這樣直接鏈式呼叫方法,不過這樣對初學者來說可以增加程式碼的可讀性,也更好實現關注分離原則。
個人看法
之前第一次看你不知的js的時候作者對物件關聯與行為委託的設計模式不斷的讚賞,而不斷對js的類與繼承的模式進行吐槽。這可能作者覺得js應該使用自己的獨有的語言風格而不是極力地模仿類的方式。個人覺得js有意讓這兩種模式都能平衡地發展,因為es6為了兩種模式都添加了新的語法,如class, Object.setPrototypeOf()方法等。所以,我覺得兩種模式看個人選擇,沒有好壞之分,重點是實現功能完成專案。