这里我们以css的封装为例
封装一个utils:
~function(){ function utils(Elem,attr){
}
return utils{
}
}()
惰性思想重写:
var utils = (function(){
var flag = 'getComputedStyle' in window;
//如果存在说明当前浏览器是标准浏览器,反之说明是IE6-IE8
function getCss(curEle,attr){
}
return {
getCss:getCss
}
})();
上面我们说是用了js惰性思想,能一次解决的,绝对不会每一次都重新处理,属于js优化的一种,那么我们还有一种惰性思想,叫做函数重载,或者叫函数重复盖,或者函数重写,还是用当前列子举例
我们现在不再封装utils
var _flag = window.getComputedStyle
function getCSS(cueEle,attr){
if(_flag){
return window.getComputedStyle(cueEle,null)[attr]
}
return cueEle.currentStyle[attr]
}
但是这样我们每次还是需要判断,我们对上面代码在进行优化
function getCSS(cueEle,attr){
if(window.getComputedStyle){
getCss = function(cueEle,attr){
return window.getComputedStyle(cueEle,null)[attr]
}
}else{
getCss = function(cueEle,attr){
return cueEle.currentStyle[attr]
}
}
return getCSS(cueEle,attr)//此处的getCss是其中的某个小方法 了
}
最开始
-
第一次执行getCss,如果是标准浏览器让
getCSS = window.getComputedStyle(cueEle,null)[attr]
,如果是IE6-8getCSS = cueEle.currentStyle[attr]
-
第二次执行getCss,直接纸执行某一个小方法,不需要在进行某种验证
--等我去玩局王者再回来写,暂且省略
重写选项卡
- 先获取tabbox,获取tabbox下所有的li,和div
- 循环所有li给li 绑定onclick事件
var tabBox = document.getElementById('tabBox');
tabList = tabBox.getElementByTagName('li');
conList = tabBox.getElementByTagName('div');
for(var i=0;i<tabList.length;i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//清除所有li选中样式
for(var k=0;k<tabList.length;k++){
tabList[k].className = null;
tabList[k].className = 'con';
}
//让当前操作的li有选中样式
this.className = 'select';
conList[this.myIndex].className = 'con select';
}
}
- 这么做可以实现,每次进入页面之后我们会把所有的li都进行清除样式,这样会浪费性能,我们必须要清除上一次操作的li就可以,所以优化过后代码如下
var tabBox = document.getElementById('tabBox');
tabList = tabBox.getElementByTagName('li');
conList = tabBox.getElementByTagName('div');
var _prev = 0;//记录上一次选中的结果,默认选中第一个
for(var i=0;i<tabList.length;i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//清除上一个li选中样式
tabList[_prev].className = null;
tabList[_prev].className = 'con';
//让当前操作的li有选中样式
this.className = 'select';
conList[this.myIndex].className = 'con select';
_prev = this.myIndex;
}
}
- 那么问题又来了,getElementByTagName会获取当前元素下的所有后代元素,而不是子元素,那我们如何获取呢,当然我们也可以采取class获取。但是原生js并没有getElementsClassName的方法,这时候考虑我们的utils;哈哈~
使用Dom库完成选项卡的封装
代码如下
var tabBox = document.getElementById('tabBox');
tab = utils.getElementsClassName('tab',tabBox)[0];
tabList = utils.children(tab,'li');
conList = utils.children(tabBox,'div');
var _prev = 0;//记录上一次选中的结果,默认选中第一个
for(var i=0;i<tabList.length;i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//清除上一个li选中样式
tabList[_prev].className = null;
tabList[_prev].className = 'con';
//让当前操作的li有选中样式
this.className = 'select';
conList[this.myIndex].className = 'con select';
_prev = this.myIndex;
}
}
为了提高代码的复用性,我们可以封装以上代码,可以用高级单利模式 html
<div class = 'tabBox'>
<ul class = 'tab clearfix'><!--页卡-->
<li></li>
</ul>
<div class='con select'><!--内容-->
</div>
<div class='con select'><!--内容-->
</div>
<div class='con select'><!--内容-->
</div>
js
~(function(){
var tabBoxList = utils.getElementsClassName('tabBox');
for(var i=0;i<tabBoxList.length;i++){
change(tabBoxList[i])
}
function change(tabBox){
var tab = utils.getElementsClassName('tab',tabBox)[0];
tabList = utils.children(tab,'li');
conList = utils.children(tabBox,'div');
var _prev = 0;//记录上一次选中的结果,默认选中第一个
for(var i=0;i<tabList.length;i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//清除上一个li选中样式
tabList[_prev].className = null;
tabList[_prev].className = 'con';
//让当前操作的li有选中样式
this.className = 'select';
conList[this.myIndex].className = 'con select';
_prev = this.myIndex;
}
}
}
})()
以上我们批量实现某一个相同结构布局的操作,可以理解为一个简单的封装,那么如何将这个疯装成插件
使用构造函数初步封装选项卡的插件
任何类库的封装,我们必用构造函数模式 js
function ChangeTab(tabBox){
//在类的任何方法都可以调用,,我们一般吧方法存在当前实例上,保证每个this都是当前类的实例
this.tabBox = tabBox;
//开始实现选项卡的功能
//ChangeTab.prototype.init.call(this) == this.init();
this.init();
}
ChangeTab.prototype = {
constructor:ChangeTab, //保证原型链的完整性
init:function(){
this.tab = utils.children( this.tabBox,'ul')[0];
this.tabList = utils.children( this.tab,'li');
this.conList = utils.children( this.tabBox,'div');
this._prev = 0;
this.change();
}
this.change(tabBox){
var tab = this.tab,
tabList =this.tabList,
conList = this.conList,
_prev = this._prev,
_this = this;
for(var i=0;i<tabList.length;i++){
//this是当前的li, _this是实例
tabList[i].myIndex = i;
tabList[i].onclick = function(){
tabList[_prev].className = null;
tabList[_prev].className = 'con';
this.className = 'select';
_this.conList[this.myIndex].className = 'con select';
_this._prev = this.myIndex;
}
}
}
}
关于this
- 非严格模式下自执行函数中this是 window,undefined
- 给元素绑定方法一般都是触发类的元素
- 方法执行看前面有没有点,点前面是谁,this就是谁,没有点this就是window
- 构造函数中的this是当前类的实例
- 使用call方法和applay可以改变this指向
- ES6中箭头函数没有this,他的this是当前他的宿主环境中的this 如上,手机核心点都在于this,我们很容易被this影响,我们必须new一个函数才可以,直接执行ChangeTab,this会指向window,为了防止冲突,我们把函数放到闭包里,且进行响应的优化
~function(){
function ChangeTab(tabBox){
this.tabBox = tabBox;
this.init();
}
ChangeTab.prototype = {
constructor:ChangeTab, //保证原型链的完整性
init:function(){ }
this.change(tabBox){}
}
window.CT = ChangeTab;
}();
var tabBoxList = utils.getElementByClassName('tabBox');
for(var i=0;i<tabBoxList.length;i++){
new CT(tabBoxList[i])
}
我们也可以增加额外的配置项
~function(){
function ChangeTab(tabBox,options){
var _default = {
initIndex : 0,//默认选中第几个
eventType : 'click' ,// 事件类型
}
for(var key in options){
if(options.hasOwnProperty(key)){
_defalt[key] = options[key];
}
}
this.initIndex = _default.initIndex;
this.eventType = _default.eventType;
this.tabBox = tabBox;
this.init();
}
ChangeTab.prototype = {
constructor:ChangeTab, //保证原型链的完整性
init:function(){
this.tab = utils.children( this.tabBox,'ul')[0];
this.tabList = utils.children( this.tab,'li');
this.conList = utils.children( this.tabBox,'div');
this._prev = 0;
this.clear();
this.change();
}
clear:function(){
//清空所有的样式类
for(var i=0;i<this.tabList.length;i++){
this.tabList[i].className = '';
this.conList[i].className = 'con';
}
//初始化默认选中页卡
this.tabList[this.initindex].className = 'select';
this.conList[this.initindex].className = 'con select';
}
change(tabBox){
var tab = this.tab,
tabList =this.tabList,
conList = this.conList,
_prev = this._prev,
_this = this;
for(var i=0;i<tabList.length;i++){
//this是当前的li, _this是实例
tabList[i].myIndex = i;
tabList[i]['on' + this.eventType} = function(){
tabList[_prev].className = null;
tabList[_prev].className = 'con';
this.className = 'select';
_this.conList[this.myIndex].className = 'con select';
_this._prev = this.myIndex;
}
}
}
}
window.CT = ChangeTab;
}();
var tabBoxList = utils.getElementByClassName('tabBox');
for(var i=0;i<tabBoxList.length;i++){
new CT(tabBoxList[i],{
initIndex : 1,//默认选中第几个
eventType : 'mouseover' ,// 事件类型
})
}
- 我们会发现构造函数
- 所有信息之间的传递都是基于实例this
- 所有的方法都在构造函数上
- 所有的值都在实例私有属性上
- 插件封装不仅仅是实现功能,而是提供更多的可变性,可变性通过传参方式搞定
- 保持函数独立性,可以接收代码实例
- 如果有额外需求,单独调用,可以在原型扩展方法
组件和插件
- 插件 只是有js功能,基本上只要导入js就可以了,具备具体的业务逻辑iscroll swiper
- ui组件 比插件大,把样式,js,结构全部规定好,可以理解成模版,相对于插件用起来简单,封装麻烦,但是可扩展性不足 bootstrop
- 类库 Dom库 jq,提供常用方法,相当于工具包 jquery zepto
- 框架 vue-mvvm react-mvm 基本具备自己独有的设计程序思想 不仅仅提供方法和相关结构 虚拟DOM渲染 效率高,方便后期维护,后期扩展,组件化模块化实现,易操作