前言:以toast组件为例子,通过三种方法实现toast类,希望对组件化开发的同学能有启发,学习代码组织的方式;对于组件化代码的开发大有裨益;
1.1代码实现
//构造函数,默认大写开头
function Toast(option){
this.prompt = ''; //toast中的内容
this.elem = null; //定义元素dom结构
this.init(option); //构造函数的入口;
}
Toast.prototype = {
// 构造器
constructor:Toast, //必须重新定义原型指向
// 初始化方法
init: function(option){
this.prompt = option.prompt || '';
this.render();
this.bindEvent();
return this; //链式操作的核心,就可以在
},
// 显示
show: function(time){
var that=this;
this.changeStyle(this.elem, 'display', 'block');
setTimeout(function(){
that.hide();
},time);
},
// 隐藏
hide: function(){
this.changeStyle(this.elem, 'display', 'none');
},
// 画出dom
render: function(){
var html = '';
this.elem = document.createElement('div');
this.changeStyle(this.elem, 'display', 'none');
html += 'x'
html += ''+ this.prompt +'
';
this.elem.innerHTML = html;
return document.body.appendChild(this.elem);
},
// 绑定事件
bindEvent: function(){
var self = this;
this.addEvent(this.elem, 'click', function(e){
if(e.target.className.indexOf('J-close') != -1){
console.log('close Toast!');
self.hide();
}
});
},
// 添加事件方法
addEvent: function(node, name, fn){
var self = this;
node.addEventListener(name,function(){
fn.apply(self,Array.prototype.slice.call(arguments));
},false);
},
// 改变样式
changeStyle: function(node, key, value){
node.style[key] = value;
}
};
var T=new Toast({prompt:'I\'m Toast!'}).show(2000); //链式操作
这里的优化,是把原型指向一个新的空对象{}
。 带来的好处,就是可以用{key:value}
的方式写原型上的方法和变量。 但是,这种方式会改变原型上构造器prototype.constructor
的指向。 如果不重新显式声明constructor
的指向,Toast.constructor.prototype.constructor
的会隐式被指向Object
。而正确的指向,应该是Toast
。 虽然通过new
实例化没有出现异常,但是在类继承方面,constructor
的指向异常,会产生不正确的继承判断结果。这是我们不希望看到的。 所以,需要修正constructor
。
1.2.实例化与new操作符
类的实例化,一个强制要求的行为,就是需要使用new操作符。如果不使用new操作符,那么构造器内的this指向,将不是当前的实例化对象。 优化的方式,就是使用instanceof
做一层防护。
function Toast(option){
if(!(this instanceof Toast)){
return new Toast(option);
}
//这里或者抛出一个错误的信息也可以
if(!(this instanceof Toast)){
throw new Error('Toast instantiation error');
}
this.prompt = '';
this.elem = null;
this.init(option);
}
1.3构造器方法/变量和原型中的方法变量之间的区别
原型上的方法和变量,是该类所有实例化对象共享的。也就是说,只有一份。 而构造器内的代码块,则是每个实例化对象单独占有。不管是否用this.**
方式,还是私有变量的方式,都是独占的。 所以,在写一个类的时候,需要考虑该新增属性是共享的,还是独占的。以此,决定在构造器还是原型上进行声明。
2.1代码实现
function Toast(){
// 已点歌曲
function init(option){
this.prompt = option.prompt || '';
render();
bindEvent();
}
function show(time){
changeStyle(this.elem, 'display', 'block');
setTimeout(function(){
hide();
},time);
}
function hide(){
changeStyle(this.elem, 'display', 'none');
}
function render(){
var html = '';
this.elem = document.createElement('div');
changeStyle(this.elem, 'display', 'none');
html += 'x'
html += ''+ this.prompt +'
';
this.elem.innerHTML = html;
return document.body.appendChild(this.elem);
}
//绑定点击事件
function bindEvent(){
var self = this;
addEvent(this.elem,'click',function(e){
if(e.target.className.indexOf('J-close') != -1){
self.hide();
}else{
alert("绑定的点击事件生效了");
}
});
}
// 添加绑定多种事件的方法
function addEvent(node,name,fn){
var self = this;
node.addEventListener(name,function(){
fn.apply(self,Array.prototype.slice.call(arguments));
},false);
}
// 改变样式
function changeStyle(node,key,value){
node.style[key]=value;
}
/**
* @date: 2018-07-24
* @Desc: 将上述方法综合起来使用,这些方法仅仅在构造函数内部进行使用,对外仅仅暴露开启和关闭方法即可
*/
var service = {
start: function(config){
init(config); //构造函数的入口;
show(20000);
},
stop: function(){
hide();
}
};
return service; //将该组件的接口暴露出去
}
var T= new Toast();
T.start({prompt:'I\'m Toast!',elem:document.getElementById("test")}); //先创建一个测试元素
2.2分析
在平时开发页面的过程中对于某一块的功能进行内部的封装,有点是代码组织简洁,需要提前规划好对外暴露的接口;
3.1代码实现
class Toast{
// 已点歌曲
init(option){
this.prompt = option.prompt || '';
render();
bindEvent();
show(2000);
}
show(time){
changeStyle(this.elem, 'display', 'block');
setTimeout(function(){
hide();
},time);
}
hide(){
changeStyle(this.elem, 'display','none');
}
render(config){
var html = '';
this.elem = document.createElement('div');
this.changeStyle(this.elem, 'display', 'block');
html += 'x'
html += ''+ config.prompt +'
';
this.elem.innerHTML = html;
return document.body.appendChild(this.elem);
}
//绑定点击事件
bindEvent(){
var self = this;
this.addEvent(this.elem,'click',function(e){
if(e.target.className.indexOf('J-close') != -1){
self.hide();
}else{
alert("绑定的点击事件生效了");
}
});
}
// 添加绑定多种事件的方法
addEvent(node,name,fn){
var self = this;
node.addEventListener(name,function(){
fn.apply(self,Array.prototype.slice.call(arguments));
},false);
}
// 改变样式
changeStyle(node,key,value){
node.style[key]=value;
}
}
var T= new Toast();
T.render({prompt:'I\'m Toast!',elem:document.getElementById("test")}); //先创建一个测试元素
T.bindEvent();
3.2代码分析
class类实现可以通过extends对类进行方法继承;