This article brings you an introduction to high-order functions in JavaScript (code examples), which has certain reference value. Friends in need can refer to it, I hope it will be helpful to you.
A function can receive another function as a parameter. In short, the parameters of the function can receive other functions. This kind of function is called a higher-order function.
Higher-order JavaScript Functions are similar to Swift’s higher-order functions
Common higher-order functions include: Map, Reduce, Filter, Sort
High-order functions refer to functions that meet at least one of the following conditions
1: Functions can be passed as parameters
2: Functions can be output as return values
Functions in the JavaScript language obviously satisfy higher-order functions Conditions, let's explore the charm of JavaScript's higher-order functions.
High-order functions implement AOP
The main function of AOP (aspect-oriented programming) is to extract some functions that have nothing to do with the core business logic module, and then use " "Dynamic weaving" method is incorporated into business modules. These functions generally include log statistics, security control, exception handling, etc. AOP is the core of Java Spring architecture. Let's explore how to implement AOP in JavaScript
Implementing AOP in JavaScript refers to "dynamically weaving" a function into another function. There are many specific implementation technologies. We use Function.prototype to do this. The code is as follows
/** * 织入执行前函数 * @param {*} fn */ Function.prototype.aopBefore = function(fn){ console.log(this) // 第一步:保存原函数的引用 const _this = this // 第四步:返回包括原函数和新函数的“代理”函数 return function() { // 第二步:执行新函数,修正this fn.apply(this, arguments) // 第三步 执行原函数 return _this.apply(this, arguments) } } /** * 织入执行后函数 * @param {*} fn */ Function.prototype.aopAfter = function (fn) { const _this = this return function () { let current = _this.apply(this,arguments)// 先保存原函数 fn.apply(this, arguments) // 先执行新函数 return current } } /** * 使用函数 */ let aopFunc = function() { console.log('aop') } // 注册切面 aopFunc = aopFunc.aopBefore(() => { console.log('aop before') }).aopAfter(() => { console.log('aop after') }) // 真正调用 aopFunc()
currying (currying)
The first thing we need to talk about about currying is what is function currying.
curring is also called partial evaluation. A curring function first accepts some parameters. After accepting these parameters, the function does not evaluate immediately, but continues to return another function. The parameters just passed in are saved in the closure formed by the function. When the function actually needs to be evaluated, all parameters passed in before are used for evaluation at once.
It’s not easy to understand the concept if you look at it rigidly. Let’s look at the next example
We need a function to calculate the consumption for 12 months in a year. We have to calculate it at the end of each month. How much money was spent. The normal code is as follows
// 未柯里化的函数计算开销 let totalCost = 0 const cost = function(amount, mounth = '') { console.log(`第${mounth}月的花销是${amount}`) totalCost += amount console.log(`当前总共消费:${totalCost}`) } cost(1000, 1) // 第1个月的花销 cost(2000, 2) // 第2个月的花销 // ... cost(3000, 12) // 第12个月的花销
To summarize, it is not difficult to find that if we want to calculate the total consumption for a year, there is no need to calculate it 12 times. We only need to perform one calculation at the end of the year. Next, we will partially curry this function to help us understand
// 部分柯里化完的函数 const curringPartCost = (function() { // 参数列表 let args = [] return function (){ /** * 区分计算求值的情况 * 有参数的情况下进行暂存 * 无参数的情况下进行计算 */ if (arguments.length === 0) { let totalCost = 0 args.forEach(item => { totalCost += item[0] }) console.log(`共消费:${totalCost}`) return totalCost } else { // argumens并不是数组,是一个类数组对象 let currentArgs = Array.from(arguments) args.push(currentArgs) console.log(`暂存${arguments[1] ? arguments[1] : '' }月,金额${arguments[0]}`) } } })() curringPartCost(1000,1) curringPartCost(100,2) curringPartCost()
Next, we will write a general curring and a function that will be cured. The code is as follows
// 通用curring函数 const curring = function(fn) { let args = [] return function () { if (arguments.length === 0) { console.log('curring完毕进行计算总值') return fn.apply(this, args) } else { let currentArgs = Array.from(arguments)[0] console.log(`暂存${arguments[1] ? arguments[1] : '' }月,金额${arguments[0]}`) args.push(currentArgs) // 返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文,这有利于匿名函数的递归或者保证函数的封装性 return arguments.callee } } } // 求值函数 let costCurring = (function() { let totalCost = 0 return function () { for (let i = 0; i < arguments.length; i++) { totalCost += arguments[i] } console.log(`共消费:${totalCost}`) return totalCost } })() // 执行curring化 costCurring = curring(costCurring) costCurring(2000, 1) costCurring(2000, 2) costCurring(9000, 12) costCurring()
Function throttling
Most functions in JavaScript are actively triggered by the user. Generally, there is no performance problem, but in some special cases they are not directly controlled by the user. It is easy for a large number of calls to cause performance problems. After all, DOM operations are very expensive. Some such scenarios will be listed below:
Let’s implement function throttling through high-order functions
/** * 节流函数 * @param {*} fn * @param {*} interval */ const throttle = function (fn, interval = 500) { let timer = null, // 计时器 isFirst = true // 是否是第一次调用 return function () { let args = arguments, _me = this // 首次调用直接放行 if (isFirst) { fn.apply(_me, args) return isFirst = false } // 存在计时器就拦截 if (timer) { return false } // 设置timer timer = setTimeout(function (){ console.log(timer) window.clearTimeout(timer) timer = null fn.apply(_me, args) }, interval) } } // 使用节流 window.onresize = throttle(function() { console.log('throttle') },600)
Time-sharing function
The throttling function provides us with a solution to limit the frequency of functions being called. Next we will encounter another problem. Some functions are actively called by users, but due to some objective reasons, these operations will seriously affect page performance. At this time, we need to use another method to solve it.
If we need to insert a large number of DOM nodes into the page in a short period of time, it will obviously be overwhelming for the browser. It may cause the browser to freeze, so we need to perform time-sharing functions and insert in batches.
/** * 分时函数 * @param {*创建节点需要的数据} list * @param {*创建节点逻辑函数} fn * @param {*每一批节点的数量} count */ const timeChunk = function(list, fn, count = 1){ let insertList = [], // 需要临时插入的数据 timer = null // 计时器 const start = function(){ // 对执行函数逐个进行调用 for (let i = 0; i < Math.min(count, list.length); i++) { insertList = list.shift() fn(insertList) } } return function(){ timer = setInterval(() => { if (list.length === 0) { return window.clearInterval(timer) } start() },200) } } // 分时函数测试 const arr = [] for (let i = 0; i < 94; i++) { arr.push(i) } const renderList = timeChunk(arr, function(data){ let p =document.createElement('p') p.innerHTML = data + 1 document.body.appendChild(p) }, 20) renderList()
Lazy loading function
In web development, some sniffing work is always inevitable due to differences in some browsers.
Because of the differences in browsers, we often need to make various compatibility improvements. Let’s take a very simple and commonly used example: event binding functions that can be used universally in various browsers.
The common way to write it is like this:
// 常用的事件兼容 const addEvent = function(el, type, handler) { if (window.addEventListener) { return el.addEventListener(type, handler, false) } // for IE if (window.attachEvent) { return el.attachEvent(`on${type}`, handler) } } 这个函数存在一个缺点,它每次执行的时候都会去执行if条件分支。虽然开销不大,但是这明显是多余的,下面我们优化一下, 提前一下嗅探的过程: const addEventOptimization = (function() { if (window.addEventListener) { return (el, type, handler) => { el.addEventListener(type, handler, false) } } // for IE if (window.attachEvent) { return (el, type, handler) => { el.attachEvent(`on${type}`, handler) } } })()
This way we can sniff before the code is loaded and then return a function. But if we put it in a public library and don't use it, it would be a bit redundant. Next we use lazy functions to solve this problem:
// 惰性加载函数 let addEventLazy = function(el, type, handler) { if (window.addEventListener) { // 一旦进入分支,便在函数内部修改函数的实现 addEventLazy = function(el, type, handler) { el.addEventListener(type, handler, false) } } else if (window.attachEvent) { addEventLazy = function(el, type, handler) { el.attachEvent(`on${type}`, handler) } } addEventLazy(el, type, handler) } addEventLazy(document.getElementById('eventLazy'), 'click', function() { console.log('lazy ') })
Once entering the branch, the implementation of the function is modified inside the function. After rewriting, the function is the function we expect and will no longer exist the next time we enter the function. Conditional branch statement.
Finally
In order to help everyone make learning easier and more efficient, I will share a large amount of information with you for free to help you become a full-stack engineer or even an architect. There are many obstacles on the road. Here I recommend a front-end full-stack learning and exchange circle for everyone: 866109386. Everyone is welcome to join the group to discuss, learn and make progress together.
When you really start learning, you will inevitably not know where to start, resulting in low efficiency and affecting your confidence in continuing to learn.
But the most important thing is that you don’t know which technologies need to be mastered, and you frequently step into pitfalls when learning, which ultimately wastes a lot of time, so it is still necessary to have effective resources.
Finally, I wish all front-end programmers who encounter bottlenecks and don’t know what to do, and wish you all the best in your future work and interviews.
The above is the detailed content of Introduction to higher-order functions in JavaScript (code examples). For more information, please follow other related articles on the PHP Chinese website!