這裡我們以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渲染 效率高,方便後期維護,後期擴充套件,元件化模組化實現,易操作