2016-01-20 48 views
2

問題和問題

我有一個小的應用程序,以異步方式從服務器獲取JSON數據,格式化爲一個表,並允許用戶執行一些簡單的操作表格(排序,篩選等)。排序行爲上傳遞函數與傳遞匿名閉包

其中一個排序函數訪問先前生成的表格(定義如下)中的DOM元素。

var SORT = function(){ 
      var my = {}; 
      // public methods 
      my.byName = function(sign){  
        (!sign) ? sign=1 : 1; 
        var T = $("#resultArea").find("tbody"); 
        var R = T.children("tr"); 
        R.sort(function(a,b){ 
          var an = $(a).attr("data-name"); 
          var bn = $(b).attr("data-name"); 
          return sign * an.localeCompare(bn); 
          }); 
        R.detach().appendTo(T); 
        } 
... 
return my; }(); 

當它指定爲一個元素的回調用戶可以點擊,我有兩個公式。

$("#sort-button").click(SORT.byName);(作爲參數傳遞函數)

OR

$("sort-button").click(function(){SORT.byName();});(通過調用該函數匿名閉包)

第一個選項失敗,但第二個作品。這裏是如何發生故障上的測試用例536行進行排序:

  • 前行2(其以字母順序排之前1)被移動到位置268。
  • 前行269移動到位置536
  • 前行536移動到位置2.

我曾嘗試和失敗,構建以同樣的方式失敗(我一旦成功將更新的問題),一個MWE。問題是:出了什麼問題?爲什麼使用匿名封閉工作?

更新

片斷的早期版本已經被消毒,以刪除參數sign和線在啓動(這將設置標誌爲1,如果其結果爲假或者是不確定的),這個想法是,通過傳遞sign=-1作爲參數,可以完成降序排序。當我從實時代碼的定義中刪除sign時,問題就消失了。

所以我發現有問題的線是(!sign) ? sign=1 : 1;當更透明if(sign !== undefined){sign=1;}取代時,問題就消失了。我認爲它的作用是將全局sign設置爲第一次通過,然後在第二次通過時定義符號,以便返回1,並且函數結束(只有一次通過已完成)。

那麼爲什麼這個錯誤不會導致匿名閉包方法崩潰?

+0

鑑於'byName'不會在任何地方使用'this',也不會接受任何參數,所以不應該有任何可觀察到的差異。 – Bergi

+0

'SORT'對象是在你附加點擊處理器之前創建的,之後從未改變過,對吧? – Bergi

+0

@Bergi正確。 SORT被定義一次,有些方法會在某些用戶事件上被調用。仍然無法重現該錯誤。數字和字母排序都會出現同樣的故障模式。 – jlovegren

回答

1

正如您發現的那樣,問題來自於sign參數。當您將一個函數作爲事件處理函數傳遞並且無需參數調用SORT.byName()時,您將獲得sign=1,並且所有內容都如預期的那樣。但是,當您直接作爲處理程序傳遞函數時,它將以Event對象作爲其參數被稱爲。突然你的sign變量中有一個對象,並且將其與一個數字相乘將產生NaN(比較函數的結果無效),這會完全混淆你的排序。

當被更透明if(sign !== undefined){sign=1;}取代時,問題就消失了。我認爲它所做的就是在第一次通過時將全局符號設置爲一個...

不是。根本沒有全局變量sign。我想你其實想要if (sign === undefined) sign = 1;。在傳遞事件時,這不起作用,因此您可能需要使用if (!Number.isFinite(sign)) sign = 1;

+0

感謝您的解釋。這也解釋了爲什麼我可以訪問事件對象(當通常直接傳遞迴調函數作爲參數時,我不會傳遞變量)。 – jlovegren

1

沒有看到更多的代碼,我真的不知道發生了什麼。您發佈的代碼片段似乎沒問題。關於你的問題,當你通過SORT.byName與使用匿名函數發送它相比時,會有細微的差別。具體而言,執行時函數中的值爲this

當你做click(SORT.byName),您發送的直接引用的功能,這意味着當它被調用的this值是什麼jQuery的處理程序click將其設置爲當它調用回調函數之前;通常這是對引發事件的元素的引用。

但是,當你做click(function() { SORT.byName(); })thisbyName值是SORT對象(但this在匿名函數仍然是任何jQuery的設置它)。這是因爲在這裏你明確地調用該函數作爲SORT對象的方法

因此,如果您的排序功能依賴this的值並假定它是SORT對象,則可能會遇到問題。

下面是一些代碼,演示了此行爲:

var obj = { 
    field: 10, 
    method: function() { 
     console.log(this); 
    } 
}; 

// The first argument to apply sets the value of "this" 
function call(f) { 
    f.apply("not this", []); 
} 

call(obj.method); //logs "not this" 
call(function() { // logs obj 
    obj.method(); 
}); 
1

通過DOM搜索和排序是不好玩。你最好保留一些狀態,排序,然後在刪除舊的結果後將新的結果附加到DOM。

var myNumbers = [ 
    [1,'one'], 
    [4,'four'], 
    [2,'two'], 
    [6,'six'], 
    [3,'three'], 
    [8,'eight'], 
    [7,'seven'], 
    [5,'five'], 
    [10,'ten'], 
    [9,'nine'] 
]; 

myNumbers.sort(function(a,b){ 
    if (a[0] > b[0]) { 
     return 1; 
    } 
    if (a[0] > b[0]) { 
     return -1; 
    } 
    return 0; 
}); 

var T = $("tbody"); 
var R = T.children("tr"); 

R.detach() 

之後,你可以在前面加上你的結果在一個循環中像你addElemnt功能,而是一個循環來代替。