如何实现链式调用
今天看了月影老师的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' })