李燕華
(北京市海淀區職工大學 北京市 100083)
Tab 標簽切換在網頁中常用在導航菜單、新聞分類、產品列表等欄目制作,應用十分廣泛。其實現的功能是:點擊不同的標簽名稱,顯示不同的欄目或內容列表。
Tab 標簽名稱和內容一一對應,所有標簽名稱樣式相同,平鋪顯示。各內容項大小、位置相同,任意時刻只有一項內容顯示在內容框中。頁面加載時第一個標簽名稱高亮顯示,第一項內容顯示在內容框中。點擊某一個標簽名稱,標簽名稱高亮顯示,其對應內容項顯示在內容框中[1]。如圖1、圖2所示。
在html 結構中,分別設置一個存放Tab 標簽名稱的列表和一個存放具體內容的列表cont,內容列表cont 包含元素的個數和Tab標簽列表包含元素的個數相同、按照編號一一對應。本文案例采用4 個Tab 標簽進行切換,其html 結構部分代碼如下:

Tab 標簽名稱樣式相同,并列平鋪,默認第一項標簽名稱高亮顯示;cont 列表中的元素大小和位置相同,只顯示第一項內容元素,其他內容元素隱藏。


圖1:頁面加載時

圖2:點擊標簽3

Tab 標簽切換的邏輯很簡單,獲取Tab 標簽列表存放到數組變量list 中、內容列表存放到數組變量cont 中,然后為list 數組中的每個標簽元素添加點擊事件,點擊事件具體的任務有兩項操作:
(1)把當前點擊的標簽名稱高亮顯示,其他標簽名稱正常顯示;
(2)顯示當前點擊的標簽對應的內容元素,其他內容元素隱藏。
因為對所有Tab 標簽進行的操作相同,可以通過循環結構完成以上操作。對每個Tab 標簽添加的點擊事件,要檢測所有的標簽和內容元素,根據其編號和當前點擊的標簽編號是否相同進行樣式設置,所以點擊事件內部也需要一層循環結構。
基于以上邏輯分析,有些程序員容易寫出以下Tab 標簽的交互代碼:


我們會發現以上代碼沒能實現Tab 標簽切換功能,通過打印循環控制變量i 的值會發現每次點擊Tab 標簽,i 的值都是4,原因點擊事件調用的函數訪問的是一個全局作用域的i,此時for 循環已經執行完畢,全局變量i=10;也就是說事件觸發之前外層循環已經執行完畢。
在ECMAScript 2015(ECMAScript 6)(后簡稱ES2015(ES6))之前,JavaScript 只有兩種作用域:全局變量與函數內的局部變量。在函數外聲明的變量作用域是全局的,全局變量在JavaScript 程序的任何地方都可以訪問。在函數內聲明的變量作用域是局部的(函數內),函數內使用 var 聲明的變量只能在函數內容訪問[2]。函數和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一起構成閉包(closure)。也就是說,閉包可以讓我們從內部函數訪問外部函數作用域。在 JavaScript 中,每當函數被創建,就會在函數生成時生成閉包[3]。此處我們需要使用使用JavaScript 函數閉包把外層循環控制變量i 的值“留住”,做法很簡單,就是讓數組中的每個函數有單獨的作用域,我們只要構造一個立即執行函數即可。[4]腳本中循環代碼如下:

通過把每個Tab 標簽的編號i 作為實參傳遞給匿名函數,閉包為每一個回調函數創建一個新的環境,把當前循環項的與事件回調相關聯起來,達到我們的預期目的。此處把循環控制變量i 的當前值與事件函數相關聯起來,點擊事件遍歷外層循環變量i,實現了Tab 標簽切換。
ES6 增加了塊級作用域的概念,ES6 新增加了兩個重要的JavaScript 關鍵字:let 和 const。其中let 聲明的變量只在 let 命令所在的代碼塊內有效,即let 聲明的變量只在 let 命令所在的代碼塊{}內有效,在{}之外不能訪問。閉包可以為每一個回調函數創建一個新的環境,把當前循環項的與事件回調相關聯起來,達到我們的預期目的。使用let 改進Tab 標簽切換的腳本代碼如下:

在外層循環中增加語句:let Tab=i,把外層循環控制變量i 的值賦給塊級變量Tab。代碼for 循環之后的{}是塊級作用域,每次循環函數引用的是其對應塊作用域的變量Tab。由于Tab 只能由循環體中的語句改變,這樣就把循環控制變量即每個標簽編號i 的值鎖住了,從而點擊事件能對每個標簽進行操作判斷和相應操作,實現了Tab 標簽切換。
閉包是JavaScript 中一個比較難理解的概念,但是它允許將函數與其所操作的某些數據(環境)關聯起來。在 Web 中,我們所寫的 JavaScript 代碼大部分是基于事件的。定義某種行為,然后將其添加到用戶觸發的事件之上(比如點擊或者按鍵),即為響應事件而執行的函數,閉包幫我們解決了以上問題。但是閉包在處理速度和內存消耗方面對腳本性能具有負面影響。
ES6 引入塊級作用域之后解決了javaScript 變量作用域的限制問題。使用let 很容易實現for 循環和基于事件的javaScript 代碼等功能,也更方便我們書寫和理解代碼。不需要像閉包那樣為每個閉包創建一個資源棧區,節省了內存消耗,提高了處理速度。
隨著前端技術的發展,大多前端框架都封裝了Tab 標簽切換等常見頁面效果,但是作為前端開發技術員,需要掌握好javaScript技術,深入理解JavaScript 的交互處理邏輯,只有這樣才能創建一個好的前端架構,保證代碼的質量。