这是我参与⌈第五届青训营⌋笔记创作活动的第2天
高阶函数、命令式编程和声明式编程
什么是高阶函数?
- 以函数作为参数
- 以函数作为返回值
- 常用于函数装饰器
let HOFO = function (fn) {
return function (...args) {
return fn.apply(this, args);
};
};
高阶函数的好处
- 高阶函数的魅力在于它的可重复利用性,如果不是高阶函数,
map、filter、reduce等强大的数组函数就不可能存在。 - 使 JavaScript 适合函数式编程的原因是它接受高阶函数。
常用高阶函数示例
防抖和节流
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
-
once
-
让函数只执行一次,常用于节流
-
let once = function (fn) { return function (...args) { if (fn) { setTimeout(() => { fn = null; }); return fn.apply(this, args); } }; };
-
-
throttle
-
让函数在执行后的一段时间不能被再次触发
-
let throttle = function (fn, time = 500) { let timer; return function (...args) { if (timer == null) { fn.apply(this, args); timer = setTimeout(() => { timer = null; }, time); } }; };
-
-
consumer
-
将多次触发的任务加入任务队列,依次间隔执行
-
function consumer(fn, time){ let tasks = [], timer; return function(...args){ tasks.push(fn.bind(this, ...args)); if(timer == null){ timer = setInterval(() => { tasks.shift().call(this) if(tasks.length <= 0){ clearInterval(timer); timer = null; } }, time) } } }
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
-
debounce
-
let debounce = function (fn, time) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { return fn.apply(this, args); }, time); }; };
-
-
非纯函数和纯函数(pure function)
什么是纯函数和非纯函数
-
纯函数是指在给定输入的参数下,输出总是确定的,不依赖于外界环境
-
例如:
let add = (a, b) => a + b; //add返回的值只与输入参数有关
-
-
非纯函数依赖上下文
-
例如:
let count = 0; let add = () => count++; //add返回的值依赖于count的值
-
如果代码中非纯函数过多,代码会变得难以维护,所以我们要减少写出非纯函数
将非纯函数转化为纯函数
const isIterable = obj => obj != null
&& typeof obj[Symbol.iterator] === 'function';
function iterative(fn) {
return function(subject, ...rest) {
if(isIterable(subject)) {
const ret = [];
for(let obj of subject) {
ret.push(fn.apply(this, [obj, ...rest]));
}
return ret;
}
return fn.apply(this, [subject, ...rest]);
}
}
这里的iterative和isIterable都是纯函数,它们能方便地对可迭代对象批量执行函数
例如假如现在有一个setColor函数能改变元素的颜色,如果这时我们要对元素批量执行,再定义一个setColors的话就产生了两个非纯函数,而如果我们利用上面的iterative函数就可以直接将setColor函数传入其中,实现批量操作,减少了非纯函数的产生。
命令式和声明式编程
以改变一个按钮的状态为例
-
命令式编程关注怎么做
-
switcher.onclick = function(evt){ if(evt.target.className === 'on'){ evt.target.className = 'off'; }else{ evt.target.className = 'on'; } }
-
-
声明式编程关注做什么
-
function toggle(...actions){ return function(...args){ let action = actions.shift(); actions.push(action); return action.apply(this, args); } } switcher.onclick = toggle( evt => evt.target.className = 'off', evt => evt.target.className = 'on' );
-
看起来区别不大但当这个按钮有三种状态时,在命令式编程下我们需要重写if-else的逻辑,而声明式编程下我们只需要在toggle里再多传入evt => evt.target.className = 'warn'就可以实现。
声明式语言在某些场景下可以极大地简化代码。通过归纳抽离部分,实现对通用代码的简化操作。而且,声明式编程通常是以数据(或者数据流)为导向的,声明式编程通常可以更好的用于处理数据。