js中實現單分派泛函式

南柯一夢同志發表於2018-11-14

有這樣一個函式,我們根據傳入的第一個引數型別不同,以不同方式執行相同操作(單分派泛函式), 我們來看一個簡單的例子:

function show(value) {
    if(value instanceof Array) {
        console.log(value.join('-'));
    } else if(value instanceof Date) {
        console.log(value * 1);
    } else if (value instanceof Number || value instanceof Boolean) {
        console.log(value * value)
    } else {
        console.log(value);
    }
}
複製程式碼

這個例子比較簡單,每一個if後面只有一行程式碼,然而在實際工作中,也許每一個if後面都有一段邏輯複雜邏輯複雜的程式碼,我們通常的處理方式就是將其拆分出來作為一個新的函式,然後在if裡面呼叫,現在我向大家介紹一種新的方法來出來這種情況。

我們先來看看使用方式:

const show = singledispatch(vlaue => console.log(value));
show
    .dispatch(value => console.log(value * value))
    .register(Number)
    .register(Boolean);
    .dispatch(value => console.log(value * 1))
    .register(Date);
    .dispatch(value => console.log(value.join('-')))
    .register(Array);

show(2); // 4
show(true); // 1
show([3, 4, 5]) // 3-4-5
show(new Date()) // 1542177163659
show({a: 1}) // [object Object] 
複製程式碼

singledispatch接受一個函式,返回一個函式物件showshow先dispatch一個函式,之後註冊一個Number和Boolean,表示show傳入的引數如果是數字或者布林型別,就呼叫dispatch的這個函式,之後也是這樣。

singledispatch需要返回一個函式物件,我們假設叫out,這個函式物件有兩個方法分別為dispatchregister,鏈式呼叫就需要返回out;具體實現過程如下,

function singledispatch(fn /* 預設是一個空函式*/ = Function.prototype) {
    let current; // 用於記錄啊dispatch的函式
    let registry = {}; // 一個物件,用於儲存某個型別下對應呼叫的函式
    function out(arg){
        let type = Object.prototype.toString.call(arg);
        if(registry[type]) {
            return registry[type](arg);
        } else {
            return fn(arg);
        }
    }

    out.dispatch = function dispatch(f) {
        if(typeof f !== 'function') {
            throw Error('some error')
        }
        current = f;
        return out;
    }

    out.register = function register(ctor) {
        if(!current) {
            throw Error('register必須在dispatch之後呼叫')
        }
        registry[`[object ${ctor.name}]`] = current;
        return out
    }

    return out;
}
複製程式碼

有的時候,我們並不是根據型別做判斷,我們希望可以自定義的方式去判斷,因此,register可以傳入一個函式,用這個函式去做判斷,就像下面這個樣子:

const f = singledispatch();

f
    .dispatch(v => v * v * v).register(v => v > 10)
    .dispatch(v => v * v).register(v => v <= 10);

f(11) // 1331
f(5) // 25

複製程式碼

此時的singledispatch需要修改registerout

function singledispatch(fn = Function.prototype) {
    let current;
    let registry = [];
    let conditions = []
    function out(arg){
        let index = conditions.findIndex(f => f(arg));
        if(index >= 0) {
            return registry[index](arg);
        } else {
            return fn(arg);
        }
    }

    out.dispatch = function dispatch(f) {
        if(typeof f !== 'function') {
            throw Error('some error')
        }
        current = f;
        return out;
    }

    out.register = function register(condition) {
        if(!current) {
            throw Error('register必須在dispatch之後呼叫')
        }
        if(typeof condition !== 'function') {
            throw Error('some error')
        }
        conditions.push(condition);
        registry.push(current);
        return out
    }

    return out;
}
複製程式碼

相關文章