C# 的 System.Linq.Enumerable 提供了強大的鏈式查詢語法 (.Where().Select().Sum()),讓集合操作聲明式、可讀性極高。JavaScript 原生陣列雖有 filter/map/reduce,但 API 風格不同。本文展示如何通過擴展 Array.prototype 實現類 LINQ 風格的 Where、Select、Sum、Average 等方法,讓 JS 代碼也能寫出 arr.Where(x => x > 3).Sum() 這樣優雅的邏輯。
Array.prototype.Where = function (func) {
var copy = [];
for (var model in this) {
try {
if (func(model)) {
copy.push(model);
}
} catch (ex) {
// 忽略不可枚舉屬性或回調異常
}
}
return copy;
};
var array = [1, 2, 3, 4, 5, 6];
var copyArray = array.Where(function (model) {
return model < 3;
});
console.log(copyArray); // [1, 2]
(function () {
// 輔助:轉為真正陣列 (避免 for...in 遍歷原型鏈屬性)
function toArray(obj) {
return Array.prototype.slice.call(obj);
}
Array.prototype.Where = function (predicate) {
return toArray(this).filter(predicate);
};
Array.prototype.Select = function (selector) {
return toArray(this).map(selector);
};
Array.prototype.Sum = function (selector) {
var arr = toArray(this);
if (selector) arr = arr.map(selector);
return arr.reduce(function (a, b) { return a + b; }, 0);
};
Array.prototype.Average = function (selector) {
var arr = toArray(this);
if (selector) arr = arr.map(selector);
return arr.length ? arr.reduce(function (a, b) { return a + b; }, 0) / arr.length : 0;
};
Array.prototype.FirstOrDefault = function (predicate) {
var arr = toArray(this);
if (predicate) arr = arr.filter(predicate);
return arr.length ? arr[0] : null;
};
Array.prototype.Any = function (predicate) {
return toArray(this).some(predicate);
};
Array.prototype.All = function (predicate) {
return toArray(this).every(predicate);
};
Array.prototype.OrderBy = function (keySelector) {
return toArray(this).sort(function (a, b) {
var ka = keySelector(a), kb = keySelector(b);
return ka > kb ? 1 : ka < kb ? -1 : 0;
});
};
Array.prototype.GroupBy = function (keySelector) {
var groups = {};
toArray(this).forEach(function (item) {
var key = keySelector(item);
(groups[key] = groups[key] || []).push(item);
});
return groups;
};
})();
var data = [
{name: 'Alice', age: 25, score: 88},
{name: 'Bob', age: 30, score: 92},
{name: 'Charlie', age: 22, score: 75},
{name: 'David', age: 28, score: 85}
];
// 篩選年齡 > 24,按分數降序,取前兩名姓名
var topTwo = data
.Where(function (x) { return x.age > 24; })
.OrderBy(function (x) { return -x.score; }) // 降序技巧
.Select(function (x) { return x.name; })
.FirstOrDefault(2); // 假設擴展 FirstOrDefault 支持 count 參數
// 實際需自行實現 Take/Skip
console.log(topTwo); // ['Bob', 'Alice']
linq(arr).Where(...) 而非擴展原型。for...in 遍歷陣列:會枚舉原型鏈屬性且順序不保證。務必用 Array.prototype.slice.call(this) 或 [...this] 轉為真陣列再用原生 filter/map/reduce。interface Array<T> { Where(predicate: (x: T) => boolean): T[]; ... } 獲得型別提示。通過幾行代碼擴展 Array.prototype,即可在 JavaScript 中享受 LINQ 風格的聲明式集合操作,極大提升代碼可讀性與表達力。生產環境建議採用成熟庫 (如 linq.js, lodash/fp, RxJS) 或自行封裝工具函數,避免原型汙染風險。