如何实现链式调用

如何实现链式调用

今天看了月影老师的PPT,感觉写的太好了,真的是循序渐进,循循善诱,醍醐灌顶,如沐春风......

特此总结一下

原PPT地址:https://ppt.baomitu.com/d/93fec210#/2

题目

html如下:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

要求第2n+1个li字体红色,第3n+1个li字体22px,绿色

小白

没错我就是小白......

function setColor (el, color) {
    el.style.color = color
}
function setFontSize (el, fontSize) {
    el.style.fontSize = fontSize
}
let items1 = document.querySelectorAll('ul > li:nth-child(2n+1)')
let items2 = document.querySelectorAll('ul > li:nth-child(3n+1)')

for (let item of items1) {
    setColor(item, 'red')
}
for (let item of items2) {
    setColor(item, 'green')
    setFontSize(item, '22px')
}

批量操作

设置一个batch函数,完成批量操作

function setColor(el, color) {
    el.style.color = color
}
function setFontSize(el, fontSize) {
    el.style.fontSize = fontSize
}
let items1 = document.querySelectorAll('ul > li:nth-child(2n+1)')
let items2 = document.querySelectorAll('ul > li:nth-child(3n+1)')

function batch (fn) {
    return function (el, ...args) {
        if (el.length >= 0) {
            return Array.from(el).map(e => fn.apply(this, [e, ...args]))
        } else {
            return fn.apply(this, [el, ...args])
        }
    }
}

setColor = batch(setColor)
setFontSize = batch(setFontSize)

setColor(items1, 'red')
setColor(items2, 'green')
setFontSize(items2, '22px')

这个API就进步了一些,不用显式地使用for循环了。

更进一步的,我们不希望显式地调用querySelectorAll,所以加一个queriable的高阶函数

可查询

function setColor(el, color) {
    el.style.color = color
}
function setFontSize(el, fontSize) {
    el.style.fontSize = fontSize
}

function batch (fn) {
    return function (el, ...args) {
        if (el.length >= 0) {
            return Array.from(el).map(e => fn.apply(this, [e, ...args]))
        } else {
            return fn.apply(this, [el, ...args])
        }
    }
}

function queriable (fn) {
    return function (selector, ...args) {
        if (typeof selector === 'string') {
            selector = document.querySelectorAll(selector)
        }
        return fn.apply(this, [selector, ...args])
    }
}

setColor = queriable(batch(setColor))
setFontSize = queriable(batch(setFontSize))

setColor('ul > li:nth-child(2n+1)', 'red')
setColor('ul > li:nth-child(3n+1)', 'green')
setFontSize('ul > li:nth-child(3n+1)', '22px')

queriable函数让我们的setColor既可以接收一个字符串,也可以接收一个dom集合。

但是对于同一个dom集合,都是改变CSS,我们写了2行代码。能否将2行代码缩减成1行呢?下面增加一个打包的pack函数

打包

function setColor(el, color) {
    el.style.color = color
}
function setFontSize(el, fontSize) {
    el.style.fontSize = fontSize
}

function batch (fn) {
    return function (el, ...args) {
        if (el.length >= 0) {
            return Array.from(el).map(e => fn.apply(this, [e, ...args]))
        } else {
            return fn.apply(this, [el, ...args])
        }
    }
}

function queriable (fn) {
    return function (selector, ...args) {
        if (typeof selector === 'string') {
            selector = document.querySelectorAll(selector)
        }
        return fn.apply(this, [selector, ...args])
    }
}

function pack (package) {
    return function (selector, obj) {
        for (let key in obj) {
            package[key].call(this, selector, obj[key])
        }
    }
}

let css = pack({color: setColor, fontSize: setFontSize})
css = queriable(batch(css))

css('ul > li:nth-child(2n+1)', {color: 'red'})
css('ul > li:nth-child(3n+1)', {color: 'green', fontSize: '22px'})

pack函数接收一个对象,把color交给setColor处理,把fontSize交给setFontSize处理;pack返回的函数接收2个参数,一个是需要操作的dom对象,一个是需要操作的CSS样式

let css = pack({color: setColor, fontSize: setFontSize})

这个时候css函数实际上还没有批量操作和可查询的功能,要想拥有这2个功能只需要经高阶函数包装一下。

css = queriable(batch(css))

链式调用

下面实现链式调用

实现链式调用的时候改一下需求,第2n+1个li的字体颜色不仅是红色,而且innerHTML改为‘abc’

链式调用的关键在于方法化,我们要把函数注册成为一个对象的方法,同时每次调用方法后仍返回这个对象。我们让这个对象是一个自定义的Chain类

function setColor(el, color) {
    el.style.color = color
}
function setFontSize(el, fontSize) {
    el.style.fontSize = fontSize
}

function setText (el, text) {
    el.innerHTML = text
}

function batch(fn) {
    return function (el, ...args) {
        if (el.length >= 0) {
            return Array.from(el).map(e => fn.apply(this, [e, ...args]))
        } else {
            return fn.apply(this, [el, ...args])
        }
    }
}

function queriable(fn) {
    return function (selector, ...args) {
        if (typeof selector === 'string') {
            selector = document.querySelectorAll(selector)
        }
        return fn.apply(this, [selector, ...args])
    }
}

function pack(package) {
    return function (selector, obj) {
        for (let key in obj) {
            package[key].call(this, selector, obj[key])
        }
    }
}

function methodize(fn) {
    return function (...args) {
        fn.apply(null, [this._selector, ...args])
        return this
    }
}

function Chain(selector) {
    this._selector = selector
}

let css = queriable(batch(pack({color: setColor, fontSize: setFontSize})))
let text = queriable(batch(setText))

Chain.prototype.css = methodize(css)
Chain.prototype.text = methodize(text)

function $(selector) {
    return new Chain(selector)
}

$('ul > li:nth-child(2n + 1)').css({ color: 'red' }).text('abc')
$('ul > li:nth-child(3n + 1)').css({ color: 'green', fontSize: '22px' })

你可能感兴趣的:(如何实现链式调用)