最近在學習js的表格排序,沒想到看不起眼的表格排序其實卻隱含了眾多JS知識點。在這裡記錄一下此次學習過程。希望對大家也有幫助。
完整的表格排序涉及了下列這些知識點:
call方法使用
sort方法深入
資料綁定
DOM映射
下面詳細的總結一下這些知識點,最後結合這些知識點數實作下面這樣一個表格排序案例。
完整的案例原始碼:https://github.com/daweihe/JS...
call方法的作用是改變方法中的this指向。
call這個方法是定義在Function.prototype
的方法。我們定義的任何一個函數都可以認為它是Function
這個類別的一個實例。那就可以透過實例的__proto__
屬性來找到所屬類別的原型。任何一個函數都可以呼叫call
和apply
等方法。
先來看一個例子:
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 op = document.getElementById("box");//获取容器 var myUl = op.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
。
最近在學習js的表格排序,沒想到看不起眼的表格排序其實卻隱含了眾多JS知識點。在這裡記錄一下此次學習過程。希望對大家也有幫助。
完整的表格排序涉及了下列這些知識點:
call方法使用
sort方法深入
資料綁定
DOM映射
下面詳細的總結一下這些知識點,最後結合這些知識點數實作下面這樣一個表格排序案例。
完整的案例原始碼:https://github.com/daweihe/JS...
call方法的作用是改變方法中的this指向。
call這個方法是定義在Function.prototype
的方法。我們定義的任何一個函數都可以認為它是Function
這個類別的一個實例。那就可以透過實例的__proto__
屬性來找到所屬類別的原型。任何一個函數都可以呼叫call
和apply
等方法。
先來看一個例子:
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 op = document.getElementById("box");//获取容器 var myUl = op.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实现表格排序,希望能帮助到大家。
jquery中tablesorter表格排序组件是如何使用的?
js表格排序实例详解(支持int,float,date,string四种数据类型)
以上是原生JS實作表格排序的詳細內容。更多資訊請關注PHP中文網其他相關文章!