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()方法等。所以,我觉得两种模式看个人选择,没有好坏之分,重点是实现功能完成项目。