封裝一個元件 + 函式惰性思想(重寫應用)

渣渣的生存之道發表於2018-05-19

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

相關文章