在 Javascript 中动态修改或生成代码

Posted

技术标签:

【中文标题】在 Javascript 中动态修改或生成代码【英文标题】:Dynamic modify or generate code in Javascript 【发布时间】:2020-03-08 19:25:06 【问题描述】:

我正在对一些表格数据进行多重排序。我想动态修改 AngularJS 站点中指定排序优先顺序的代码行。 (我只是在修改数组的结构,因为 AngularJS 会观察绑定数据的变化并在 html 显示中动态地反映它们。)

对于多重排序,我使用thenBy.js。本质上,您创建一个以 firstBy(comparativeFunction) 开头的变量,然后您可以根据需要添加任意数量的 thenBy(comparativeFunction)

我会根据需要动态添加和删除排序比较器。这是我从这个简单的两级多排序开始的地方:

// I want to modify and append to this line
var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) );

它最初会根据“状态”属性中的数据对行进行排序,然后根据 ID 编号的“#”进行子排序。

我想知道的是如何在上面的行中动态添加更多代码。例如,假设我想添加一个三级排序,比如“唯一 ID”。我可以这样对行进行硬编码:

var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) )
  .thenBy( (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]) );

我只是不确定如何以编程方式将更多 'thenBy()' 方法附加到这一行。我应该生成一个字符串并通过 eval() 运行它吗?有没有更好(更安全)的方法?

这是目前的节目:

self.dataSource = [
    "#": "1", "Unique ID": "100130", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available",
    "#": "2", "Unique ID": "100131", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 2", "Status": "Available",
    "#": "3", "Unique ID": "100132", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 3", "Status": "Available",
    "#": "4", "Unique ID": "100133", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 4", "Status": "Available",
    "#": "5", "Unique ID": "100134", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 5", "Status": "Checked Out",
    "#": "6", "Unique ID": "100135", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 6", "Status": "Checked Out",
    "#": "7", "Unique ID": "100136", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 7", "Status": "Checked Out",
    "#": "8", "Unique ID": "100137", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 8", "Status": "Checked Out",
    "#": "9", "Unique ID": "100138", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available",
    "#": "10", "Unique ID": "100139", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 1", "Status": "Available",
    "#": "11", "Unique ID": "100140", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 2", "Status": "Available",
    "#": "12", "Unique ID": "100141", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 3", "Status": "Lost",
    "#": "13", "Unique ID": "100142", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 4", "Status": "Lost",
    "#": "14", "Unique ID": "100143", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 1", "Status": "Available",
    "#": "15", "Unique ID": "100144", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 2", "Status": "Available",
    "#": "16", "Unique ID": "100145", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 1", "Status": "Checked Out",
    "#": "17", "Unique ID": "100146", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 2", "Status": "Available",
    "#": "18", "Unique ID": "100147", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 3", "Status": "Available",
    "#": "19", "Unique ID": "100148", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 4", "Status": "Checked Out",
    "#": "20", "Unique ID": "100149", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 5 - Box 1 - Position 1", "Status": "Available"       
];

var self = this;

self.multiSortDict = ;

self.addSort = function(sortName, sortComparatorFunction)

    self.multiSortDict[sortName] = sortComparatorFunction;


// Example code to add a bunch of sorting methods
self.addSort("Status",(a, b) => a["Status"].localeCompare(b["Status"]));
self.addSort("#", (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]));
self.addSort("Name",(a, b) => a["Name"].localeCompare(b["Name"]));
self.addSort("Unique ID", (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]));
self.addSort("Position",(a, b) => a["Position"].localeCompare(b["Position"]));


self.rebuildMultiSort = function (multiSortDict) 
    var sortList = Object.values(multiSortDict);

    var sortMethod = firstBy( sortList[0] ); // Grab the first entry to set the 'firstBy()' method

    for (var i = 1; i < sortList.length; i++)  // Starting at second position
        // I want to dynamically modify this line, being able to append as many 'thenBy()' statements as necessary
        // remove the semicolon from the previous sortMethod assignment
        // append the new 'thenBy()' to assignment
        sortMethod +=
            .thenBy(value);
    
    return sortMethod;

使用上面的例子,理想情况下我会得到一个类似这样的代码行:

var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) )
  .thenBy( "Name",(a, b) => a["Name"].localeCompare(b["Name"]) )
  .thenBy( (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]) )
  .thenBy( "Position",(a, b) => a["Position"].localeCompare(b["Position"]) )

有什么建议吗?

【问题讨论】:

【参考方案1】:

在几乎所有情况下,都有比使用evil 更好的做事方式。

我建议查找比较函数,例如

const ordersLookup = 
  Status: (a, b) => a["Status"].localeCompare(b["Status"]),
  "#": (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]),
  Name: (a, b) => a["Name"].localeCompare(b["Name"]),
  // ...

然后当你决定要排序时:

let order = ["Name", "Status"];

并从查找中选择适当的条目,并减少链:

let s = order.reduce((a, e) => a.thenBy(ordersLookup[e]), firstBy(() => 0));

let orderFunctions = order.map(e => ordersLookup[e]);
let [firstInOrder, *restInOrder] = orderFunctions;
let s = restInOrder.reduce((a, e) => a.thenBy(e), firstBy(firstInOrder));

(最终结果应该相同,性能和可读性略有不同)。

无需生成代码。

另外,请注意 thenBy.js 知道如何处理按属性排序,因此您不需要自己编写大部分函数。 firstBy("Name").thenBy("#") 应该开箱即用。

【讨论】:

谢谢。这个例子在我修改后起作用了。这似乎正在做我正在寻找的工作!

以上是关于在 Javascript 中动态修改或生成代码的主要内容,如果未能解决你的问题,请参考以下文章

使用 JavaScript 或 PHP 动态生成 CSS 网格

动态加载 JavaScript 文件

动态加载 JavaScript 文件

如何使用javascript验证动态生成的输入框值

在 ASP.NET MVC 中动态生成 Javascript、CSS

更新面板内动态添加的用户控件内部的Javascript函数未定义