这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战。
前言
30-seconds 是一个学习系列,基本都是通过简短的代码实现某些功能, 包含
- 30-seconds-of-code
满足所有开发需求的简短JavaScript代码片段 - 30-seconds-of-css
满足所有开发需求的简短CSS代码片段 - 30-seconds-of-react
满足所有开发需求的简短React代码片段
等等,还有很多系列, 我们今天的主题是 30-seconds-of-code , 即简短的代码段。
挑选了我觉得比较有意思或者有意义的15个代码,我们一起开始简短代码之旅吧!
精选
URLJoin
地址拼接。
除此之外,URLSearchParams
或URL
都可以很好的处理QueryString, 更多详情参见 私藏的这些高级工具函数,你拥有几个?
const URLJoin = (...args) =>
args
.join('/')
.replace(/[\/]+/g, '/')
.replace(/^(.+):\//, '$1://')
.replace(/^file:/, 'file:/')
.replace(/\/(\?|&|#[^!])/g, '$1')
.replace(/\?/g, '&')
.replace('&', '?');
示例
URLJoin('http://www.google.com', 'a', '/b/cd', '?foo=123', '?bar=foo');
// 'http://www.google.com/a/b/cd?foo=123&bar=foo'
uncurry
减少嵌套函数调用次数,改变函数的传参方式。 这是反柯里化吗?
这个函数非常灵活,很有意思。
const uncurry = (fn, n = 1) => (...args) => {
const next = acc => args => args.reduce((x, y) => x(y), acc);
if (n > args.length) throw new RangeError('Arguments too few!');
return next(fn)(args.slice(0, n));
};
const add = x => y => z => x + y + z;
const uncurriedAdd1 = uncurry(add, 1);
uncurriedAdd1(1)(2)(3) // 6
const uncurriedAdd1 = uncurry(add, 2);
uncurriedAdd1(1,2)(3) // 6
const uncurriedAdd3 = uncurry(add, 3);
uncurriedAdd3(1, 2, 3); // 6
onScrollStop
监听滚动停止事件,当停止滚动的时候,执行回调函数。
需要在停止滚动后,执行某些操作,还是很有用的。
const onScrollStop = callback => {
let isScrolling;
window.addEventListener(
'scroll',
e => {
clearTimeout(isScrolling);
isScrolling = setTimeout(() => {
callback();
}, 150);
},
false
);
};
onScrollStop(() => {
console.log('The user has stopped scrolling');
});
byteSize
返回字符串字节数。
都知道,中文和英文所占的字节数是不一样的,有占两个的,三个的,四个的,一个的。
英文一般一个字节,中文一般三个。
const byteSize = str => new Blob([str]).size;
byteSize('a') // 1
byteSize('س') // 2
byteSize('中') // 3
byteSize('😀'); // 4
byteSize('Hello World'); // 11
这还涉及 Unicode编码的知识,为了验证更多字符,你可以查阅 Unicode对应表
runPromisesInSeries
顺序执行promise, 实际上应该生成Promise的函数。
const runPromisesInSeries = ps =>
ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
runPromisesInSeries([() => delay(1000), () => delay(2000)]);
// Executes each promise sequentially, taking a total of 3 seconds to complete
这个版本,不能传参,我在 私藏的这些高级工具函数,你拥有几个?, 实现了一个带参数版本。
function runPromises(promiseCreators, initData) {
return promiseCreators
.reduce((promise, next) => promise
.then((data) => next(data))
, Promise.resolve(initData));
}
var promise1 = function (data = 0) {
return new Promise(resolve => {
resolve(data + 1000);
});
}
var promise2 = function (data) {
return new Promise(resolve => {
resolve(data -500);
});
}
runPromises([promise1, promise2], 1).then(res=>console.log(res)); // 501
stringifyCircularJSON
将包含循环引用的 JSON 对象序列化为 JSON 格式。
其思路是使用WeakSet
保存数据进行对比。
const stringifyCircularJSON = obj => {
const seen = new WeakSet();
return JSON.stringify(obj, (k, v) => {
if (v !== null && typeof v === 'object') {
if (seen.has(v)) return;
seen.add(v);
}
return v;
});
};
const obj = { n: 42 };
obj.obj = obj;
stringifyCircularJSON(obj); // '{"n": 42}'
UUIDGeneratorBrowser
UUID生成器,除此之外URL.createObjectURL也可以生成UUID。这两种算是比较主流的方式。
const UUIDGeneratorBrowser = () =>
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
UUIDGeneratorBrowser(); // '7982fcfe-5721-4632-bede-6000885be57d'
function genUUID() {
const url = URL.createObjectURL(new Blob([]));
// const uuid = url.split("/").pop();
const uuid = url.substring(url.lastIndexOf('/')+ 1);
URL.revokeObjectURL(url);
return uuid;
}
genUUID() // cd205467-0120-47b0-9444-894736d873c7
addDaysToDate
时间添加天数,返回的是字符串。
const addDaysToDate = (date, n) => {
const d = new Date(date);
d.setDate(d.getDate() + n);
return d.toISOString().split('T')[0];
};
addDaysToDate('2020-10-15', 10); // '2020-10-25'
addDaysToDate('2020-10-15', -10); // '2020-10-05'
compose
从右到左的复合函数。 没记错的话和 redux的compose 极其类似。
毕竟嘛,思路是一样的。
onst compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)));
const add5 = x => x + 5;
const multiply = (x, y) => x * y;
const multiplyAndAdd5 = compose(
add5,
multiply
);
multiplyAndAdd5(5, 2); // 15
deepGet
获取对象的多级属性,属性参数是数组。
const deepGet = (obj, keys) =>
keys.reduce(
(xs, x) => (xs && xs[x] !== null && xs[x] !== undefined ? xs[x] : null),
obj
);
let index = 2;
const data = {
foo: {
foz: [1, 2, 3],
bar: {
baz: ['a', 'b', 'c']
}
}
};
deepGet(data, ['foo', 'foz', index]); // get 3
deepGet(data, ['foo', 'bar', 'baz', 8, 'foz']); // null
当然 lodash 也提供了 .get, 当然实现的复杂度也高很对,其属性参数用的是字符串。
objectToQueryString
把对象转为 queryString,就这个queryString的转换,就有好几个牛气冲天的库。
下载量过 千万的 query-string 和 500万的 qs, 主要解决两个问题,其中一个就是 toQueryString。
const objectToQueryString = queryParameters => {
return queryParameters
? Object.entries(queryParameters).reduce(
(queryString, [key, val], index) => {
const symbol = queryString.length === 0 ? '?' : '&';
queryString +=
typeof val === 'string' ? `${symbol}${key}=${val}` : '';
return queryString;
},
''
)
: '';
};
objectToQueryString({ page: '1', size: '2kg', key: undefined });
// '?page=1&size=2kg'
parseCookie
把cookie转为键值对。
作为一个操作前端,操作cookie是日常操作,我相信肯定有不少同志引入了第三方库,其实只需代几码就可以的。
const parseCookie = str =>
str
.split(';')
.map(v => v.split('='))
.reduce((acc, v) => {
acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
return acc;
}, {});
parseCookie(document.cookie)
// {_octo: "GH1.1.2059864283.1626332708", tz: "Asia/Shanghai"}
parseCookie('foo=bar; equation=E%3Dmc%5E2');
// { foo: 'bar', equation: 'E=mc^2' }
unfold
使用迭代器函数和初始种子值构建数组。
在造假数据或者随机数据的时候,比较有用。
const unfold = (fn, seed) => {
let result = [],
val = [null, seed];
while ((val = fn(val[1]))) result.push(val[0]);
return result;
};
var f = n => (n > 50 ? false : [-n, n + 10]);
unfold(f, 10); // [-10, -20, -30, -40, -50]
triggerEvent
触发给定元素上的特定事件,可以选择传递自定义数据。
这个在IE11以及一些低版本是有问题的,低版本使用的是 document.createEvent
const triggerEvent = (el, eventType, detail) =>
el.dispatchEvent(new CustomEvent(eventType, { detail }));
triggerEvent(document.getElementById('myId'), 'click');
triggerEvent(document.getElementById('myId'), 'click', { username: 'bob' });
repeatGenerator
创建生成器,无限重复给定值。 有点意思,实用场景嘛,也许测试吧。
当然,中途你可以更新值。 这里想告诉大家的是 genetator有入参的概念。
const repeatGenerator = function* (val) {
let v = val;
while (true) {
let newV = yield v;
if (newV !== undefined) v = newV;
}
};
const repeater = repeatGenerator(5);
repeater.next(); // { value: 5, done: false }
repeater.next(); // { value: 5, done: false }
repeater.next(4); // { value: 4, done: false }
repeater.next(); // { value: 4, done: false }
写在最后
如果你觉得不错,你的一赞一评就是我前行的最大动力。
技术交流群请到 这里来。 或者添加我的微信 dirge-cloud,一起学习。