1 实现 compose ✔️
// 用法如下:
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x + 2;
}
function fn3(x) {
return x + 3;
}
function fn4(x) {
return x + 4;
}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1 + 4 + 3 + 2 + 1 = 11
实现啦
function compose(...fns) {
// compose 返回的是 (...args) => next(prev(...args)) 函数
return fns.reduce((prev, next) => {
return (...args) => next(prev(...args));
});
}
2 setTimeout 模拟实现 setInterval ✔️
setInterval 不可控,比如有个场景,我们要每秒一次轮询后端接口,如果我们使用定时器而且接口比较慢时,比如 3 秒返回一次,那么就可能并行很多无意义请求,服务器压力会很大,改成 setTimeout 去实现,我们可以控制当上个请求结束后,再开始计时,也就是说,我们能实现接口返回后,再间隔一秒去发请求,这样看起来正常了许多。
const newInterval = (func, ms) => {
let timer = null;
const inside = () => {
timer = setTimeout(() => {
// 包到异步任务里,这样能实现 func(); 执行之后,下次任务才开始计时
func();
inside();
}, ms);
}
inside();
return () => clearTimeout(timer);
}
测试代码~
function fn() {
console.log('😘');
}
let timer = newInterval(fn, 1000);
2-2 变形:setInterval 模拟 setTimeout ✔️
const mySetTimeout = (fn, ms) => {
let timer = setInterval(() => {
clearInterval(timer);
fn();
}, ms);
}
测试
function fn() {
console.log('🍊');
}
mySetTimeout(fn, 2000);
3 发布订阅模式 ✔️
class eventEmitter {
constructor() {
this.eventMap = {};
}
$on(name, cb) {
let events = this.eventMap[name];
if (!events) {
this.eventMap[name] = [cb]
} else {
events.push(cb);
}
}
$emit(name, data) {
let events = this.eventMap[name];
if (!events) return;
for (var i = 0; i < events.length; i++) {
events[i](data);
}
}
// 注册只可以触发一次的事件
$once(name, cb) {
const fn = () => {
cb();
this.$off(name);
}
this.$on(name, fn);
}
$off(name) {
this.eventMap[name] && (delete this.eventMap[name]);
}
}
测试代码
let e = new eventEmitter();
e.$on('jump', () => console.log('跳起来~'));
e.$on('sayHi', () => console.log('hi~'));
e.$on('sayHi', () => console.log('hi2~'));
e.$once('onceSmile', () => console.log('我只笑一次'));
e.$off('jump');
e.$emit('jump');
e.$emit('sayHi', 'babalala');
e.$emit('onceSmile');
e.$emit('onceSmile');
4. 观察者模式 ✔️
class Sub {
constructor(name) {
this.name = name;
this.watchers = [];
this.state = '😊';
}
add(watcher) {
this.watchers.push(watcher);
}
notify(newState) {
this.watchers.map(watcher => watcher.update(newState));
}
}
class Watcher {
constructor() {
this.name = name;
}
update(newState) {
console.log('👶🏻新状态来了', newState);
}
}
测试代码
let parent = new Sub('父亲');
let mom = new Sub('母亲');
let baby = new Watcher('宝宝');
parent.add(baby);
mom.add(baby);
parent.notify('宝宝哭了');
5 数组去重 ✔️
var nums = [1, 1, 2, 3, 4, 4, 5, 6, 5, 3, 0, 1]
// @1 方案1
let res = [...new Set(nums)]
// @2 hash 大法,挨个收集
function removeRepeatElm() {
let map = {};
let res = [];
for (var i = 0; i < nums.length; i++) {
let cur = nums[i];
if (!map[cur]) {
map[cur] = 1;
res.push(cur);
}
}
return res;
}
console.log(removeRepeatElm(nums));
6 数组扁平化 ✔️
递归解法1
平平无奇,又妙趣横生。
const flatFn = (arr, total = []) => {
if (!arr.length) return [];
let toString = Object.prototype.toString;
for (let i = 0; i < arr.length; i++) {
if (toString.call(arr[i]) == '[object Array]') {
flatFn(arr[i], total);
} else {
total.push(arr[i]);
}
}
return total;
}
递归解法2
利用 reduce 简化逻辑。
const flatFn = arr => {
return arr.reduce((pre, cur) => {
return Array.isArray(cur) ? [...pre, ...flatFn(cur)] : [...pre, cur]
}, []);
}
迭代解法
不说了,牛逼。
// 巧用 [].concat(1, 2, [3]) => [1, 2, 3]
function flatter(arr) {
if (!arr.length) return;
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
7 寄生组合继承 ✔️
function Parent () {
this.name = 'parent';
}
Parent.prototype = {
sayName() {
console.log('i am Parent');
}
}
function Children() {
Parent.call(this);
this.age = 12;
}
Children.prototype = Object.create(Parent.prototype);
const ys = new Children();
console.log(ys);