最近在學習js的表格排序,沒想到看不起眼的表格排序實際上卻暗含了衆多JS知識點。在這裏記錄一下這次學習過程。但願對你們也有所幫助。css
完整的表格排序涉及了下列這些知識點:git
下面詳細的總結一下這些知識點,最後結合這些知識點實現下面這樣一個表格排序案例。github
完整的案例源碼:https://github.com/daweihe/JS...面試
call方法的做用是改變方法中的this指向。ajax
call這個方法是定義在Function.prototype
的方法。咱們定義的任何一個函數均可以認爲它是Function
這個類的一個實例。那麼就能夠經過實例的__proto__
屬性找到所屬類的原型。任何一個函數均可以調用call
和apply
等方法。數組
先來看一個例子:app
var obj = { name : 'JS' } function testCall () { console.log(this); } testCall.call( obj ); // {name: "JS"}
首先函數testCall
經過原型鏈查找機制找到call方法執行,call方法在執行過程當中把調用call方法這個函數實例中的this都改變成call的第一個參數,接下來調用call方法的這個實例函數執行。函數
看兩個題目:性能
function fn1() { console.log(1); console.log(this); } function fn2() { console.log(2); console.log(this); } fn1.call(fn2); //this -> fn2 fn1.call.call(fn2); //這裏的call是改變function.__proto__.call的call方法中的this,至關於執行參數
call方法在執行的時候,call方法的第一個參數是用來改變this的,而從第二個參數開始都是傳給調用call的函數的參數。學習
在非嚴格模式下,給call方法不傳遞參數、或者傳遞null、undefined後,this都是指向window
。
sum.call(); //window sum.call(null); //window sum.call(undefined); //window
嚴格模式下call執行的時候和非嚴格模式不一樣:
sum.call(); //undefined sum.call(null); //null sum.call(undefined); //undefined
下面使用call方法實現一個類數組轉換爲數組的方法:
function listToArray (likeAry) { var ary = []; try { ary = Array.prototype.slice.call(likeAry); } catch (e) { for (var i = 0; i < likeAry.length; i ++) { ary[ary.length] = likeAry[i]; } } return ary; }
和call相似的方法還有apply和bind方法,這裏簡單總結一下。
apply方法的做用和call方法如出一轍,只是傳參的形式不太同樣,apply將函數的參數用數組包裹起來:
function sum(num1, num2) { console.log(num2 + num1); console.log(this); } sum.apply(null,[100,200]);
bind方法一樣也是用來改變this關鍵字的,可是它只是僅僅改變this指向,不當即執行調用this的函數。
function sum(num1, num2) { console.log(num2 + num1); console.log(this); } var obj = {name : 'zx'} var temp = sum.bind(obj); //temp已是被改變了this的函數 temp(100,200); //當咱們須要的時候才執行 //或者像這樣處理 var temp = sum.bind(null, 100, 200); temp();
bind方法體現了js中的預處理思想。
咱們知道數組的sort
方法只能排序10之內的數組。若是須要排序的數組中存在大於10的數字,咱們就須要向sort
方法中傳入回調函數,常見的是這樣:
ary.sort(function (a,b) { return a - b; });
這樣就能實現數組的升序排序。那麼這樣排序的原理究竟是什麼呢?
對於傳入的兩個參數:a
表明的是找到的數組中的當前項,b
表明的是當前項的後一項。
return a -b
: 若是a大於b,返回結果,a與b交換位置。若是a小於b,那麼a和b位置不變。 這是升序排序return b -a
: 若是b大於a,返回結果,a與b交換位置。若是a小於b,那麼a和b位置不變。 這是降序排序瞭解了基本原理後,對於這樣一個二維數組,如何實現按年齡排序?
var persons = [{ name:'dawei', age:55 },{ name:'ahung', age:3 },{ name:'maomi', age:2 },{ name:'heizi', age:78 },{ name:'afu', age:32 }];
其實很簡單:
ary.sort(function(a,b){ return a.age - b.age; });
若是按姓名排序,則要涉及字符串的localeCompare()
方法:
ary.sort(function(a,b){ return a.name.localeCompare(b.name); });
name.localeCompare()
這個方法會根據兩個字符串的字母進行比較,若是前一個字符串的第一個字母在24個英文字母中出現的位置比後一個字符串的第一個字符出現的位置靠前,則認定第一個字符串小,返回-1
。若是出現的位置靠後,則認定第一個字符串大,返回1。若是所比較的字符相等。則比較下一個字符。
這個方法很實用,經常使用於按姓氏排序,對於漢字,該方法會自動將漢字轉換爲漢語拼音進行比較。
在js中通常使用動態綁定或者拼接字符串的方式實現數據綁定。
動態綁定:
//ary爲須要添加到頁面中的數據數組 var oDiv = document.getElementById("box");//獲取容器 var myUl = oDiv.getElementsByTagName("ul")[0];//獲取列表 var arrLength = ary.length; for (var i = 0;i < arrLength ; i ++) { //動態建立元素 var oli = document.createElement("li"); oli.innerHTML = '<span>' + (i + 5) + '</span>' + ary[i].title; myUl.appendChild(oli);//動態添加元素 }
每添加一次就會引發一次DOM迴流,若是數據量過大,這樣則會嚴重影響性能。
關於DOM的迴流與重繪,推薦你們看一下這篇文章:http://www.css88.com/archives...
拼接字符串:
var str = ""; for(var i=0; i<ary.length; i++){ str += '<li>'; str += '<span>'; str += (i+5); str += '</span>'; str += ary[i].title; str += '</li>'; } myUl.innerHTML += str;
這種方式雖然只引發一次迴流,可是它會去除原來存在的元素中全部的事件和屬性。若是咱們爲列表中的li標籤添加鼠標移入,背景變色的事件,那麼這種方法會使這個事件失效。
爲了解決上面的兩種數據綁定方法帶來的問題,咱們使用文檔碎片來添加數據。
var frg = document.createDocumentFragment();//建立文檔碎片 for (var i =0; i <ary.length ;i ++ ){ var li = document.createElement("li"); li.innerHTML = '<span>' + ( i + 5 ) + '</span>' + ary[i].title; frg.appendChild(li);//將數據動態添加至文檔碎片中 } myUl.appendChild(frg); //將數據一次性添加到頁面中 frg = null; //釋放內存
這樣即只引發一次DOM迴流,又會保留原來存在的事件。
DOM映射機制:所謂映射,就是指兩個元素集之間元素相互「對應」的關係。頁面中的標籤集合和在JS中獲取到的元素對象(元素集合)就是這樣的關係。若是頁面中的HTML標籤結構發送變化,那麼集合中對應的內容也會跟着自動改變。
<ul id="myul"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul>
對於這樣一個列表使用下列腳本:
var myul = document.getElementById("myul"); var mylis = myul.getElementsByTagName('li'); for (var i = mylis.length - 1 ; i >= 0; i --) { myul.appendChild(mylis[i]); } console.log(mylis.length); // 5
將獲取到的列表元素反序從新插入ul中,那麼ul列表會變成下面這樣:
<ul id="myul"> <li>5</li> <li>4</li> <li>3</li> <li>2</li> <li>1</li> </ul>
咱們看到列表的長度依然是5,只是位置顛倒了。這是由於每一個li標籤和JS中獲取的標籤對象存在一個對應關係,當某個標籤被從新插入到頁面中時,頁面中對應的標籤會移動到插入的位置。這就是DOM映射。
之因此使用動態獲取數據,是爲了使用文檔碎片綁定數據。
var res = ''; //聲明一個全局變量,接收數據 var xhr = new XMLHttpRequest(); xhr.open('get', 'date.txt', false); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { res = JSON.parse(xhr.responseText); } } xhr.send(null);
此時數據就保存在了res
這個全局變量之中。
var frg = document.createDocumentFragment(); for (let i = 0; i < res.length; i++) { var tr = document.createElement("tr"); for (key in res[i]) { var td = document.createElement("td"); td.innerHTML = res[i][key]; tr.appendChild(td); } frg.appendChild(tr); } tbody.appendChild(frg);
這裏涉及的點較多
//爲兩列添加點擊事件 for (let i = 0; i < ths.length; i++) { let curTh = ths[i]; curTh.sortFlag = -1; //用於對列進行升降序排列 curTh.index = i; //記錄當前點擊列的索引,便於排序操做 if (curTh.className == 'sort') { curTh.onclick = function() { sort.call(this); //改變排序函數內this的指向,讓其指向當前點擊列 } } } //排序方法 function sort() { //對數組元素進行排序 let target = this; //這裏將this取出,由於在sort方法裏須要使用該this,可是sort方法裏的this是調用方法的數組 this.sortFlag *= -1; //1 表明升序 -1表明降序 let ary = listToArray(bodyTrs); //獲取body數據 ary = ary.sort(function(a, b) { let one = a.cells[target.index].innerHTML; let two = b.cells[target.index].innerHTML; let oneNum = parseFloat(one); let twoNum = parseFloat(two); if (isNaN(oneNum) || isNaN(two)) { return one.localeCompare(two) * target.sortFlag; } else { return (oneNum - twoNum) * target.sortFlag; } }); //把排好序的數組從新寫入頁面 let frg = document.createDocumentFragment(); for (let i = 0; i < ary.length; i++) { rg.appendChild(ary[i]); } tbody.appendChild(frg); frg = null; //點擊某列時,要將其餘列的排序標誌恢復爲-1,讓下次再點擊任意一個標籤時都是默認是升序排列 for (let i = 0; i < ths.length; i++) { if (ths[i] != this) { ths[i].sortFlag = -1; } } }
表格排序應用很常見,在面試中也會有這樣的題目。這個小案例作下來,受益不淺。這是我在學習的某峯學院的JS課程中的一個案例,若是對JS掌握不紮實的同窗,歡迎保存:連接: https://pan.baidu.com/s/1jHVy8Uq 密碼: v4jk
。若是連接失效,加Q羣領取:154658901
。