JavaScript高阶函数的用法和使用场景
Posted 三水草肃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高阶函数的用法和使用场景相关的知识,希望对你有一定的参考价值。
高阶函数
高阶函数需要满足的条件:
- 函数可以作为参数传递,
- 函数可以作为返回值输出。
函数作为参数传递
场景:回调函数。
作用:可以抽离数据(易变化)业务逻辑,把数据业务逻辑放在函数参数中,这样可以分离变化和不变的部分。
- 例子:当我们想在 ajax 请求之后接受数据,但是不知道何时请求完成,那么我们可以使用回调函数 callback 作为参数传入,请求完成之后执行 callback 函数
const getInfo = function (useId, callback)
Api("xx" + useId, function (data)
if (status === 200)
callback(data);
);
;
getInfo(123, function (data)
console.log(data);
);
- 回调函数不仅在异步请求中,当一个函数不适合执行一些请求时,可以把这些请求封装成函数,把它作为函数传递给另外一个函数,委托另一个函数来执行
- 隐藏节点的逻辑放在回调函数中,我们可以控制隐藏节点,当节点创建好之后就会执行我们的回调函数
const appendDiv = function (divLen, callback)
for (let i = 0; i < divLen; i++)
const div = document.createElement("div");
div.innerhtml = 1;
document.body.appendChild(div);
// div.style.display = "none"; // 比这种好了很多
if (typeof callback === "function")
callback(div);
;
appendDiv(100, function (node)
node.style.display = "none";
);
函数作为返回值输出
函数作为返回值输出的应用场景更多,也更能体现函数式编程的巧妙。函数返回一个可执行的函数,意味着过程是可延续的。
1.判断数据的类型
const isType = function (type)
return function (obj)
return Object.prototype.toString.call(obj) === `[object $type]`;
;
;
console.log(isType("String")("123")); // true
高阶函数实现 AOP
AOP(面向切面编程)的主要作用是保证业务逻辑高内聚性。
- 把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常兜底。把这些功能抽离出来之后,再通过动态织入 1 的方式渗入业务逻辑中。
Function.prototype.before = function (beforeFn)
const _self = this;
return function ()
beforeFn.apply(this, arguments); // 执行 before 新函数
return _self.apply(this, arguments); // 执行 func 函数
;
;
Function.prototype.after = function (afterFn)
const _self = this;
return function ()
let ret = _self.apply(this, arguments);
afterFn.apply(this, arguments); // 执行 func 函数
return ret; // 执行 after 函数
;
;
const func = function ()
console.log(2);
;
func
.before(function ()
console.log(1);
)
.after(function ()
console.log(3);
)();
高阶函数的其他应用场景
- currying 函数柯里化
调用函数时,明确带上了参数,会把参数保存起来,当我们不传参数时,才开始计算
// 场景:多次输入之后,计算结果值。比如每月花销,我只要月底计算得出结果就可以。这时候就可以用到函数柯里化
const cost = function ()
const args = [];
return function ()
if (arguments.length === 0)
let res = args.reduce((p, n) => p + n);
return res;
else
[].push.apply(args, arguments);
;
;
const costs = cost();
costs(100);
costs(100);
console.log(costs());
// 使用高阶函数,抽离一下柯里化逻辑
const curry = function (fn)
const args = [];
return function ()
if (arguments.length === 0)
return fn.apply(this, args);
else
[].push.apply(args, arguments);
return arguments.callee;
;
;
const kcost = (function ()
return function ()
return [...arguments].reduce((p, n) => p + n);
;
)();
const cost1 = curry(kcost);
cost1(100);
cost1(100);
cost1(100);
console.log(cost1());
- uncurrying 让对象调用不属于自己的方法
call、apply、bind 能解决这个问题const obj1 = name: "obj1", ; const obj2 = getName: function () console.log(this.name); , ; obj2.getName.call(obj1);
- 常借用 Array.prototype 的方法。Array.prototype 方法原本只可以操作 array 数组,但是通过 call、apply、bind 就可以把任意对象当作 this 传入
(function ()
Array.prototype.push.call(arguments, 4);
console.log(arguments); // [1, 2, 3, 4]
)(1, 2, 3);
- 实现 uncurrying,把范化的 this 提取出来
1. 实现第一种
Function.prototype.uncurrying = function ()
const self = this;
return function ()
const obj = Object.prototype.shift.call(arguments); // 提取this
return self.apply(obj, arguments);
;
;
const myPush = Array.prototype.push.myUncurrying();
const arrPush = [1, 2, 3];
(function ()
myPush(arguments, 4);
console.log([...arguments]); // [1, 2, 3, 4]
)(1, 2, 3);
const obj =
0: 1,
length: 1,
;
myPush(obj, 2);
console.log(obj); // '0': 1, '1': 2, length: 2
1. 实现第二种uncurrying
Function.prototype.myUncurry2 = function()
const self = this;
return function()
return Function.prototype.call.apply(self, arguments);
-
函数节流
-
函数被频繁调用。 函数本身的实现不合理,导致的函数频繁调用的性能问题。
-
函数节流解决的问题:函数被触发的频率太高,那么我们就要通过函数节流限制触发频率。节流可以用截流来理解,比如河流流速很快,影响河道通行安全,是不是要通过截流操作减缓河道流速。
-
场景
- window.resize()
当浏览器窗口大小改变,就会触发 window.resize()。如果在 window.resize()里做一些 DOM 相关的操作,就会非常消耗性能的。界面可能会造成卡顿 - mousemove()
当给 DOM 节点绑定了 mousemove()拖拽事件,如果在 mousemove()里做一些 DOM 相关的操作,就会非常消耗性能的。界面可能会造成卡顿 - 上传进度
上传进度会频繁通知用户,一秒 10 次。很显然并不需要这么频繁提示用户
- window.resize()
-
函数节流实现原理:执行的函数使用 setTimeout 延迟执行。如果延迟执行还没有完成,则不执行接下来函数。
throttle 函数接受两个参数,第一个参数时需要被延迟执行的函数,第二个参数是延迟执行时间
const throttle = function (fn, delay) const self = fn; // 保存需要延迟执行的函数 let timer, firstTime = true; return function () const args = arguments; const that = this; if (firstTime) self.apply(that, args); return (firstTime = false); if (timer) return false; timer = setTimeout(function () clearTimeout(timer); timer = null; self.apply(that, args); , delay); ; ; function fn() console.log(1); window.onresize = throttle(fn, 500);
-
-
分时函数
函数节流是用户主动调用导致的,也有一些客观原因导致严重影响性能的。比如一次性渲染上千上万的 DOM 节点,浏览器是吃不消的,会导致卡顿甚至假死
解决方案是:- 分批渲染节点,之前一秒渲染 1000 节点,改为 200ms 渲染 10 节点
- 用户可视范围内渲染节点,与虚拟列表的实现原理一致
const TimeChunk = function (ary, fn, count, delay) let obj, t; const len = ary.length; const start = function () for (let i = 0; i < Math.min(count, ary.length); i++) obj = ary.shift(); fn(obj); ; return function () t = setInterval(function () if (ary.length === 0) return clearInterval(t); start(); , delay); ; ; const ary = []; for (let i = 1; i <= 1000; i++) ary.push(i); const renderDomList = TimeChunk( ary, function (n) const div = document.createElement("div"); div.innerHTML = n; document.body.appendChild(div); , 10, 200 ); renderDomList();
以上是关于JavaScript高阶函数的用法和使用场景的主要内容,如果未能解决你的问题,请参考以下文章