1. 程式人生 > 實用技巧 >js類與繼承與物件關聯風格解讀

js類與繼承與物件關聯風格解讀

前言

讀完了《你不知的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()方法等。所以,我覺得兩種模式看個人選擇,沒有好壞之分,重點是實現功能完成專案。