前端項目模塊化的實踐3:使用 TypeScript 的收益

如下是關於前端項目模塊化的實踐,包含如下內容:javascript

  1. 搭建 NPM 私有倉庫管理源碼及依賴
  2. 使用 Webpack 打包基礎設施代碼
  3. 使用 TypeScript 編寫可靠類庫

使用 Webpack 打包基礎設施代碼已經很大程度上解決了生產力,但日益複雜業務和邏輯仍然讓前端陷入「動態一時爽、重構火葬場」的笑談,TypeScript 爲解決這個問題而來。html

在本章節咱們使用 TypeScript 完成一個相似 LINQ 中 Enumerable<T> 的實現,涉及代碼編寫與測試用例,仍然不深刻關於 TypeScript 的討論。前端

場景

假想須要實現這樣一個功能:好比一組學生,咱們但願按照根據班級分組,接着按年齡排序,最後按照名稱排序。java

這並不複雜,這些步驟是有前後的,每步操做獲得的都是一組對象集合,分組和排序是常規數組操做,寫幾個循環就能夠達到目標。git

C# 的實現

void Main() {
    var students = new List<Student> {
        new Student { Classes =  "class-1", Name = "Rattz", Age = 11 },
        new Student { Classes =  "class-2", Name = "Rose", Age = 10 },
        new Student { Classes =  "class-1", Name = "Mike", Age = 11 }
    };
    
    var seq = students.GroupBy(x => x.Classes)
        .Select(g => new {
            Classes = g.Key,
            Members = g.OrderBy(x => x.Age)
                .ThenBy(x => x.Name)
                .Select(x => x)
        });
}

class Student {
    public String Classes { get; set; }
    public String Name { get; set; }
    public Int32 Age { get; set; }
}

得力於靜態語言的類型保證及 LINQ 語法對內存對象的操做能力,一行代碼就簡單而有表現力地解決了問題,在 LINQPad 裏組織以下:es6

ES6 很明瞭但代碼略多

如今來看 JavaScript 的寫法,使用 ES6 的 Map 對象極大地減化了代碼。github

let students = [
    {'classes': 'class-1', 'name': 'Rattz', 'age': 11},
    {'classes': 'class-2', 'name': 'Rose', 'age': 10},
    {'classes': 'class-1', 'name': 'Mike', 'age': 11},
];

let map = new Map();
for (let item of students) {
    let classes = map.get(item.classes);
    if (Object.is(classes, undefined)) {
        classes = [item];
        map.set(item.classes, classes);
    }
    else {
        classes.push(item);
    }
}

for (let [, value] of map.entries()) {
    value.sort((p1, p2) => {
        if (p1.age !== p2.age) {
            return p1.age - p2.age;
        }
        return p1.name.localeCompare(p2.name);
    });
}

let groups = [];
for (let [key, value] of map.entries()) {
    groups.push({
        classes: key,
        members: value,
    });
}

ES5 reduce 很強有力但很難一次編寫正確

代碼略多,包含了3個循環,若是不使用 Map 代碼就要進行數組查找;若是想壓縮循環,就使用 Array.prototype.reduce 函數,代碼就很難懂了。typescript

let students = [
    {'classes': 'class-1', 'name': 'Rattz', 'age': 11},
    {'classes': 'class-2', 'name': 'Rose', 'age': 10},
    {'classes': 'class-1', 'name': 'Mike', 'age': 11},
];

let groups = students.reduce((previous, current) => {
    let arr = previous.filter(x => x.key === current.classes);
    if (arr.length > 0) {
        arr[0].members.push(current);
    }
    else {
        previous.push({
            classes  : current.classes,
            members: [current],
        });
    }
    return previous;
}, []);

for(let g of groups) {
    g.members.sort((a, b) => a.name.localeCompare(b.name));
}

TypeScript 的使用

下文經過編寫類庫完成相似功能,咱們充分使用生產力而先不考慮語言、版本問題,看看具體的調用部分c#

interface Student {
    classes: string,
    name: string
    age: number
}

let students: Student[] = [
    {'classes': 'class-1', 'name': 'Rattz', 'age': 11},
    {'classes': 'class-2', 'name': 'Rose', 'age': 10},
    {'classes': 'class-1', 'name': 'Mike', 'age': 11},
];

let groups = Enumerable.from(students)
    .groupBy(x => x.classes)
    .select(x => ({
        classes: x.key,
        members: Enumerable.from(<Student[]>x.items)
            .sortBy((x, y) => x.age - y.age)
            .thenSortBy((x, y) => x.name.localeCompare(y.name))
    }));

完整代碼見於 Enumerable 行 100 左右。數組

若是 Enumerable 的實現是 JavaScript 版本,這部分代碼可能充滿了疑問,即使閱讀源碼也很難一會兒理解實現者的用意

  • groupby 須要什麼樣的參數?
  • select 使用了 groupBy 返回值,它包含怎樣的數據結構?
  • sortBy 但返回了什麼?
  • thenSortBy 如何進行二次排序,又返回了什麼?

TypeScript 可以解答上述問題問題,如下是函數簽名。

  • groupBy:完整簽名是 groupBy<K, U>(keySelector: (value: T, index: number) => K, valueSelector?: (value: T, index: number) => U): Enumerable<Group<K, T | U>>,雖然稍長可是閱讀起來也就那回事
    • keySelector: 接受2個參數(分別是數組元素和索引)返回1個值,一般是對象的某個屬性,
    • valueSelector: 和keySelector類似,表示獲得新元素的方法,術語是「投影」
    • Enumerable<Group<K, T | U>>groupBy 的返回類型,表示仍然是 Enumerable<> 實例,只是內部元素由傳入的 valueSelectorkeySelector 決定,該簽名使得鏈式調用成爲可能;
  • select: 完整簽名是 select<U>(callback: (value: T, index: number) => U): Enumerable<U>,和 groupBy 相似,但更加簡單
  • sortBy: 和 Array.prototype.sort 功能類似,但簽名是 sortBy(compareFn: (a: T, b: T) => number): OrderedEnumerable<T>,返回 OrderedEnumerable<> 實例
  • thenSortBy:同 sortBy,依賴 OrderedEnumerable<T> 的內部實現

咱們能夠在安全地傳入參數和引用返回值,在代碼編寫階段就能獲得編譯器的語法、值入參數合法性檢查。

相關文章
相關標籤/搜索