ホームページ  >  記事  >  ウェブフロントエンド  >  ネイティブ JS はテーブルのソートを実装します

ネイティブ JS はテーブルのソートを実装します

小云云
小云云オリジナル
2017-12-06 14:52:032925ブラウズ

私は最近 JS でのテーブル ソートを学んでいましたが、目立たないテーブル ソートが実際に多くの JS の知識を暗示しているとは思いませんでした。この学習プロセスをここに記録します。皆さんにも役立つことを願っています。

完全なテーブルソートには、次の知識ポイントが含まれます:

  • を使用した呼び出しメソッド

  • sortメソッドの詳細

  • データバインディング

  • DOMマッピング

以下は、これらの詳細な知識をまとめたものです。最後にこれらの知識ポイントを組み合わせて、次のテーブルの並べ替えケースを実装します。
ネイティブ JS はテーブルのソートを実装します

完全なケースソースコード: https://github.com/daweihe/JS...

1. 知識ポイントのまとめ

1. callメソッドの機能は次のとおりです。これが指すメソッドを変更します。

call このメソッドは Function.prototype で定義されています。私たちが定義する関数はすべて、Function クラスのインスタンスと見なすことができます。次に、インスタンスの __proto__ 属性を通じてクラスのプロトタイプを見つけることができます。どの関数でも callapply などのメソッドを呼び出すことができます。

まず例を見てみましょう: Function.prototype的方法。我们定义的任何一个函数都可以认为它是Function这个类的一个实例。那么就可以通过实例的__proto__属性找到所属类的原型。任何一个函数都可以调用callapply等方法。

先来看一个例子:

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 : &#39;zx&#39;}

var temp = sum.bind(obj);   //temp已经是被改变了this的函数
temp(100,200);              //当我们需要的时候才执行


//或者像这样处理
var temp = sum.bind(null, 100, 200);
temp();

bind方法体现了js中的预处理思想。

2、 sort排序深入

我们知道数组的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:&#39;dawei&#39;,
    age:55
},{
    name:&#39;ahung&#39;,
    age:3
},{
    name:&#39;maomi&#39;,
    age:2
},{
    name:&#39;heizi&#39;,
    age:78
},{
    name:&#39;afu&#39;,
    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

//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 = &#39;<span>' + (i + 5) + '</span>' + ary[i].title;
    myUl.appendChild(oli);//动态添加元素
}
まず、関数 testCall は、実行プロセス中に、プロトタイプ チェーン検索メカニズムを通じて実行する呼び出しメソッドを見つけます。呼び出しメソッドは、関数内でこれを変更します。 call メソッドを呼び出すインスタンス その後、call の最初のパラメータが呼び出されて、call メソッドのインスタンス関数が実行されます。

2 つの質問を見てください:

var str = "";
for(var i=0; i<ary.length; i++){
    str += &#39;<li>';
    str += '<span>';
    str += (i+5);
    str += '</span>';
    str += ary[i].title;
    str += '</li>';
}

myUl.innerHTML += str;

call メソッドが実行されるとき、call メソッドの最初のパラメータはこれを変更するために使用され、2 番目のパラメータから開始して、呼び出しを呼び出す関数に渡されるパラメータです。

非厳密モードでは、呼び出しメソッドにパラメータが渡されない場合、または null または未定義が渡された場合、これは window を指します。

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;  //释放内存
厳密モードでの呼び出しの実行は、非厳密モードの実行とは異なります:

<ul id="myul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
以下では、call メソッドを使用して、配列のような配列を配列に変換するメソッドを実装します:

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
次のようなメソッドcall には apply メソッドと binding メソッドも含まれていますが、結論から言えばこれらは単純です。

apply メソッドの機能は call メソッドとまったく同じですが、apply は関数のパラメータを配列でラップする形式が異なります。

<ul id="myul">
    <li>5</li>
    <li>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ul>
bind メソッドは this キーワードを変更するためにも使用されます。これは this のポイントを変更するだけであり、this を呼び出す関数はすぐには実行されません。

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);
bind メソッドは、js での前処理のアイデアを具体化します。

2. 詳細な並べ替え🎜🎜 配列の sort メソッドは 10 個以内の配列のみを並べ替えることができることがわかっています。並べ替える必要がある配列内に 10 より大きい数値がある場合、コールバック関数を sort メソッドに渡す必要があります。一般的な関数は次のとおりです。 🎜
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);
🎜 このように、配列は昇順にソートできます。では、この並べ替えの背後にある原理は何でしょうか? 🎜🎜渡される 2 つのパラメーターについて: a は見つかった配列内の現在の項目を表し、b は現在の項目の後の項目を表します。 🎜🎜🎜🎜return a -b: a が b より大きい場合、結果を返し、a と b の位置を交換します。 a が b より小さい場合、a と b の位置は変更されません。 これは昇順です🎜🎜🎜🎜return b -a: b が a より大きい場合、結果を返し、a と b の位置を交換します。 a が b より小さい場合、a と b の位置は変更されません。 これは降順です🎜🎜🎜🎜基本原理を理解した後、このような2次元配列の場合、年齢でソートするにはどうすればよいでしょうか? 🎜
//为两列添加点击事件
for (let i = 0; i < ths.length; i++) {
    let curTh = ths[i];
    curTh.sortFlag = -1; //用于对列进行升降序排列
    curTh.index = i; //记录当前点击列的索引,便于排序操作
    if (curTh.className == &#39;sort&#39;) {
        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;
        }
    }
}
🎜実際は非常に簡単です: 🎜
var obj = {
    name : &#39;JS&#39;
}

function testCall () {
    console.log(this);
}

testCall.call( obj );     // {name: "JS"}
🎜名前で並べ替える場合、文字列の localeCompare() メソッドが関係します: 🎜
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,相当于执行参数
🎜name.localeCompare()このメソッドの比較は、24 個の英語文字のうち、前の文字列の最初の文字が後の文字列の最初の文字よりも前に出現する場合、最初の文字が最初に考慮されます。小さい場合は、-1 が返されます。後で出現する場合は、最初の文字列の方が大きいとみなされ、1 が返されます。比較された文字が等しい場合。次に、次の文字を比較します。 🎜🎜この方法は非常に実用的で、漢字の場合、比較のために中国語の文字を中国語のピンインに自動的に変換するためによく使用されます。 🎜🎜3. データバインディング🎜🎜 js では、データバインディングを実装するために、通常、動的バインディングまたは文字列スプライシングが使用されます。 🎜🎜動的バインディング: 🎜
sum.call(); //window
sum.call(null); //window
sum.call(undefined); //window
🎜 追加するたびに DOM リフローが発生します。データ量が多すぎると、パフォーマンスに重大な影響を及ぼします。 🎜🎜 DOM リフローと再描画については、この記事を読むことをお勧めします: http://www.css88.com/archives... 🎜🎜文字列の結合: 🎜
sum.call(); //undefined
sum.call(null); //null
sum.call(undefined); //undefined
🎜この方法ではリフローが 1 回だけ発生しますが、削除されます。元の要素のすべてのイベントと属性。マウスが移動して背景の色が変わるときにリスト内の li タグのイベントを追加すると、このメソッドはこのイベントを無効にします。 🎜🎜上記 2 つのデータ バインディング方法によって引き起こされる問題を解決するために、ドキュメント フラグメントを使用してデータを追加します。 🎜
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;
}
🎜これにより、DOM リフローが 1 つだけ発生し、元の既存のイベントが保持されます。 🎜

4、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映射。

二、实现表格排序

1、使用ajax获取数据

之所以使用动态获取数据,是为了使用文档碎片绑定数据。

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这个全局变量之中。

2、使用文档碎片绑定数据

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);

3、对表格进行排序

这里涉及的点较多

//为两列添加点击事件
for (let i = 0; i < ths.length; i++) {
    let curTh = ths[i];
    curTh.sortFlag = -1; //用于对列进行升降序排列
    curTh.index = i; //记录当前点击列的索引,便于排序操作
    if (curTh.className == &#39;sort&#39;) {
        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 の知識を暗示しているとは思いませんでした。この学習プロセスをここに記録します。皆さんにも役立つことを願っています。

完全なテーブルソートには、次の知識ポイントが含まれます:

  • を使用した呼び出しメソッド

  • sortメソッドの詳細

  • データバインディング

  • DOMマッピング

以下は、これらの詳細な知識をまとめたものです。最後にこれらの知識ポイントを組み合わせて、次のテーブルの並べ替えケースを実装します。
ネイティブ JS はテーブルのソートを実装します

完全なケースソースコード: https://github.com/daweihe/JS...

1. 知識ポイントのまとめ

1. callメソッドの機能は次のとおりです。これが指すメソッドを変更します。

call このメソッドは Function.prototype で定義されています。私たちが定義する関数はすべて、Function クラスのインスタンスと見なすことができます。次に、インスタンスの __proto__ 属性を通じてクラスのプロトタイプを見つけることができます。どの関数でも callapply などのメソッドを呼び出すことができます。

まず例を見てみましょう: Function.prototype的方法。我们定义的任何一个函数都可以认为它是Function这个类的一个实例。那么就可以通过实例的__proto__属性找到所属类的原型。任何一个函数都可以调用callapply等方法。

先来看一个例子:

var obj = {
    name : &#39;JS&#39;
}

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 : &#39;zx&#39;}

var temp = sum.bind(obj);   //temp已经是被改变了this的函数
temp(100,200);              //当我们需要的时候才执行


//或者像这样处理
var temp = sum.bind(null, 100, 200);
temp();

bind方法体现了js中的预处理思想。

2、 sort排序深入

我们知道数组的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:&#39;dawei&#39;,
    age:55
},{
    name:&#39;ahung&#39;,
    age:3
},{
    name:&#39;maomi&#39;,
    age:2
},{
    name:&#39;heizi&#39;,
    age:78
},{
    name:&#39;afu&#39;,
    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

//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 = &#39;<span>' + (i + 5) + '</span>' + ary[i].title;
    myUl.appendChild(oli);//动态添加元素
}
まず、関数 testCall は、実行プロセス中に、プロトタイプ チェーン検索メカニズムを通じて実行する呼び出しメソッドを見つけます。呼び出しメソッドは、関数内でこれを変更します。 call メソッドを呼び出すインスタンス その後、call の最初のパラメータが呼び出されて、call メソッドのインスタンス関数が実行されます。

2 つの質問を見てください:

var str = "";
for(var i=0; i<ary.length; i++){
    str += &#39;<li>';
    str += '<span>';
    str += (i+5);
    str += '</span>';
    str += ary[i].title;
    str += '</li>';
}

myUl.innerHTML += str;

call メソッドが実行されるとき、call メソッドの最初のパラメータはこれを変更するために使用され、2 番目のパラメータから開始して、呼び出しを呼び出す関数に渡されるパラメータです。

非厳密モードでは、呼び出しメソッドにパラメータが渡されない場合、または null または未定義が渡された場合、これは window を指します。

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;  //释放内存
厳密モードでの呼び出しの実行は、非厳密モードの実行とは異なります:

<ul id="myul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
以下では、call メソッドを使用して、配列のような配列を配列に変換するメソッドを実装します:

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
次のようなメソッドcall には apply メソッドと binding メソッドも含まれていますが、結論から言えばこれらは単純です。

apply メソッドの機能は call メソッドとまったく同じですが、apply は関数のパラメータを配列でラップする形式が異なります。

<ul id="myul">
    <li>5</li>
    <li>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ul>
bind メソッドは this キーワードを変更するためにも使用されます。これは this のポイントを変更するだけであり、this を呼び出す関数はすぐには実行されません。

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);
bind メソッドは、js での前処理のアイデアを具体化します。

2. 詳細な並べ替え🎜🎜 配列の sort メソッドは 10 個以内の配列のみを並べ替えることができることがわかっています。並べ替える必要がある配列内に 10 より大きい数値がある場合、コールバック関数を sort メソッドに渡す必要があります。一般的な関数は次のとおりです。 🎜
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);
🎜 このように、配列は昇順にソートできます。では、この並べ替えの背後にある原理は何でしょうか? 🎜🎜渡される 2 つのパラメーターについて: a は見つかった配列内の現在の項目を表し、b は現在の項目の後の項目を表します。 🎜🎜🎜🎜return a -b: a が b より大きい場合、結果を返し、a と b の位置を交換します。 a が b より小さい場合、a と b の位置は変更されません。 これは昇順です🎜🎜🎜🎜return b -a: b が a より大きい場合、結果を返し、a と b の位置を交換します。 a が b より小さい場合、a と b の位置は変更されません。 これは降順です🎜🎜🎜🎜基本原理を理解した後、このような2次元配列の場合、年齢でソートするにはどうすればよいでしょうか? 🎜
//为两列添加点击事件
for (let i = 0; i < ths.length; i++) {
    let curTh = ths[i];
    curTh.sortFlag = -1; //用于对列进行升降序排列
    curTh.index = i; //记录当前点击列的索引,便于排序操作
    if (curTh.className == &#39;sort&#39;) {
        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;
        }
    }
}
🎜実際は非常に簡単です: 🎜
var obj = {
    name : &#39;JS&#39;
}

function testCall () {
    console.log(this);
}

testCall.call( obj );     // {name: "JS"}
🎜名前で並べ替える場合、文字列の localeCompare() メソッドが関係します: 🎜
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,相当于执行参数
🎜name.localeCompare()このメソッドの比較は、24 個の英語文字のうち、前の文字列の最初の文字が後の文字列の最初の文字よりも前に出現する場合、最初の文字が最初に考慮されます。小さい場合は、-1 が返されます。後で出現する場合は、最初の文字列の方が大きいとみなされ、1 が返されます。比較された文字が等しい場合。次に、次の文字を比較します。 🎜🎜この方法は非常に実用的で、漢字の場合、比較のために中国語の文字を中国語のピンインに自動的に変換するためによく使用されます。 🎜🎜3. データバインディング🎜🎜 js では、データバインディングを実装するために、通常、動的バインディングまたは文字列スプライシングが使用されます。 🎜🎜動的バインディング: 🎜
sum.call(); //window
sum.call(null); //window
sum.call(undefined); //window
🎜 追加するたびに DOM リフローが発生します。データ量が多すぎると、パフォーマンスに重大な影響を及ぼします。 🎜🎜 DOM リフローと再描画については、この記事を読むことをお勧めします: http://www.css88.com/archives... 🎜🎜文字列の結合: 🎜
sum.call(); //undefined
sum.call(null); //null
sum.call(undefined); //undefined
🎜この方法ではリフローが 1 回だけ発生しますが、削除されます。元の要素のすべてのイベントと属性。マウスが移動して背景の色が変わるときにリスト内の li タグのイベントを追加すると、このメソッドはこのイベントを無効にします。 🎜🎜上記 2 つのデータ バインディング方法によって引き起こされる問題を解決するために、ドキュメント フラグメントを使用してデータを追加します。 🎜
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;
}
🎜これにより、DOM リフローが 1 つだけ発生し、元の既存のイベントが保持されます。 🎜

4、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映射。

二、实现表格排序

1、使用ajax获取数据

之所以使用动态获取数据,是为了使用文档碎片绑定数据。

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这个全局变量之中。

2、使用文档碎片绑定数据

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);

3、对表格进行排序

这里涉及的点较多

//为两列添加点击事件
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学习总结经典小案例之表格排序

jquery中tablesorter表格排序组件是如何使用的?

js表格排序实例详解(支持int,float,date,string四种数据类型)

以上がネイティブ JS はテーブルのソートを実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。