Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are JavaScript closures and higher order functions?

2025-03-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

It is believed that many inexperienced people have no idea about what JavaScript closures and higher-order functions are like. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.

"in JavaScript, functions are first-class citizens". We can always see this sentence in various books and articles.

Since there is a first class, then of course there is a second class.

If citizens are graded, first-class citizens can do anything, and second-class citizens can't do this and that. JavaScript's function is also an object, can have attributes, can be assigned to a variable, can be put in an array as an element, can be used as an attribute of other objects, and can do anything. It can do what other objects can do, and it can do what other objects can't do. This is the status of first-class citizens.

-Cheng Mo Morgan

So what it means is: functions, like other ordinary objects, have properties and methods, and functions can do what ordinary objects can do.

Because of the great freedom in JavaScript, functions are endowed with excellent expression and flexibility, but there are also a lot of problems that make people scratch their cheeks. In this paper, we will discuss the two most common concepts closely related to functions: closures and higher-order functions. These two concepts are also often encountered in later articles on design patterns.

1.1 what is a closure

A closure occurs when the function can remember and access the lexical scope in which it is located, even if the function is executed outside the current lexical scope.

Let's first look at an example of a closure:

Function foo () {var a = 2 function bar () {console.log (a)} return bar} var baz = foo () baz () / output: 2

The foo function passes a function bar, and the passed bar is assigned to baz and called. Although baz is executed outside the scope of foo, baz can access the internal scope of the foo where the previous bar function is called.

Because bar declares that it is inside the foo function, bar has a closure that covers the internal scope of foo, so that the internal scope of foo has not been reclaimed. In general, the entire internal scope of a function is destroyed after execution, because JavaScript's GC (Garbage Collection) garbage collection mechanism automatically reclaims unused memory space. But closures prevent some GC, such as foo () in this case, from being executed, because the returned bar function still holds a reference to its scope, so its internal scope is not recycled.

Note: if you don't have to use a closure, avoid creating it as much as possible, because closures have a negative impact on performance in terms of processing speed and memory consumption.

1.2 implement result caching using closures (memo mode)

Memo mode is a typical application of the characteristics of applying closures. For example, there is a function:

Function add (a) {return a + 1;}

When you run add () multiple times, the result is recalculated each time, which is more performance-consuming if it is a costly calculation operation. Here, you can cache the input that has been calculated.

So here you can use the characteristics of closures to implement a simple cache, using an object to store input parameters inside the function. If you enter the same parameters next time, compare the properties of the object. If there is a cache, just take the value out of the object.

/ * memo function * / function memorize (fn) {var cache = {} return function () {var args = Array.prototype.slice.call (arguments) var key = JSON.stringify (args) return cache [key] | (cache [key] = fn.apply (fn) Args)}} / * complex calculation function * / function add (a) {return a + 1} var adder = memorize (add) adder (1) / / output: 2 current: cache: {'[1]': 2} adder (1) / / output: 2 current: cache: {'[1]': 2} adder (2) / / output: 3 current: cache: {'[1]': 2 '[2]': 3}

The way to use ES6 is more elegant:

/ * memo function * / function memorize (fn) {const cache = {} return function (... args) {const key = JSON.stringify (args) return cache [key] | | (cache [key] = fn.apply (fn) Args)}} / * complex calculation function * / function add (a) {return a + 1} const adder = memorize (add) adder (1) / / output: 2 current: cache: {'[1]': 2} adder (1) / / output: 2 current: cache: {'[1]': 2} adder (2) / / output: 3 current: cache: {'[1]': 2 '[2]': 3}

Explain a little bit:

In the cheat function, JSON.stringify is used to serialize the parameters passed to the adder function into a string, regard it as the index of cache, and pass the result of the add function as the value of the index to cache, so that if the passed parameter has been passed before, then the cached calculation result is returned, and there is no need to calculate it. If the passed parameter has not been calculated, the fn.apply (fn, args) is calculated and cached. Then return the result of the calculation.

Of course, if the implementation here is to be applied in practice, it needs to be further improved, such as:

The cache cannot be expanded forever, which consumes too much memory resources. We can cache only the newly incoming n.

When using in the browser, we can use the persistence means of the browser to persist the cache, such as cookie, localStorage and so on.

The complex calculation function here can be a state of the past, such as an operation on a target, so that the past state is cached and the state is easily rolled back.

A complex calculation function can also be an asynchronous operation with a slow return time, so that if the result is cached, it can be fetched directly locally next time, rather than re-making an asynchronous request.

Note: cache cannot be Map, because keys of Map are compared using = =, so when reference type values are passed in as keys, although they look equal, they are not, for example, [1]! = = [1], so they are still saved as different keys.

/ / X error demonstration function memorize (fn) {const cache = new Map () return function (... args) {return cache.get (args) | cache.set (args, fn.apply (fn) Args) .get (args)} function add (a) {return a + 1} const adder = memorize (add) adder (1) / / 2 cache: {[1] = > 2} adder (1) / / 2 cache: {[1] = > 2} adder (2) / / 3 cache: {[1] = > 2, [1] = > 2, [2] = > 3}

two。 Higher order function

A higher-order function is a function that has a function in the input parameter, or a function whose output is a function.

2.1 function as a parameter

If you've used setTimeout, setInterval, or ajax requests, you've already used higher-order functions, and this is the most common scenario we see: a callback function because it passes a function as an argument to another function.

For example, in ajax requests, we usually use callback functions to define the operation logic when the request succeeds or fails:

$.ajax ("/ request/url", function (result) {console.log ("request successful!") })

There are many manipulation methods on the prototypes of basic objects such as Array, Object, String, and so on, and callback functions can be accepted to easily manipulate objects. Here is a very common Array.prototype.filter () method, which returns a newly created array containing all array elements that return true or true values after the callback function is executed.

Var words = ['spray',' limit', 'elite',' exuberant', 'destruction',' present']; var result = words.filter (function (word) {return word.length > 6}) / / output: ["exuberant", "destruction", "present"]

Another application of callback functions is hooks. If you have used frameworks such as Vue or React, you should be familiar with hooks. It looks like this:

Function foo (callback) {/ /... Some operations callback ()}

2.2 function as the return value

Another frequently seen scenario for higher-order functions is to output one function inside another, such as:

Function foo () {return function bar () {}}

Closures are mainly used to maintain scope:

Function add () {var num = 0 return function (a) {return num = num + a}} var adder = add () adder (1) / output: 1 adder (2) / / output: 3

1. Corey

Currying, also known as Partial Evaluation, transforms an original function that accepts multiple parameters into a function that accepts a single parameter (the first parameter of the original function), and returns a new function, which can accept the remaining parameters and finally return the same result as the original function.

The core idea is to split the multi-parameter function into a single (or part) parameter function, and then return to call the next single (or part) parameter function, and deal with the remaining parameters in turn.

Collization has three common functions:

Parameter reuse

Return ahead of time

Delayed calculation / operation

Let's first take a look at the general implementation of Corey:

/ / ES5 mode function currying (fn) {var rest1 = Array.prototype.slice.call (arguments) rest1.shift () return function () {var rest2 = Array.prototype.slice.call (arguments) return fn.apply (null, rest1.concat (rest2)}} / / ES6 mode function currying (fn,... rest1) {return function (... rest2) {return fn.apply (null) Rest1.concat (rest2))}}

Use it to Corify a sayHello function:

/ connect function sayHello (name, age, fruit) {console.log (console.log (`my name is ${name}, I am ${age} years old, I like to eat ${fruit} `)} var curryingShowMsg1 = currying (sayHello, 'Xiaoming') curryingShowMsg1 (22, 'Apple') / / output: my name is Xiaoming, I am 22 years old, I like to eat var curryingShowMsg2 = currying (sayHello, 'bad') 20) curryingShowMsg2 ('watermelon') / / output: my name is Xiao Shuai, I am 20 years old, I like to eat watermelons

For higher-order usage, see: JavaScript functional programming skills-Coreization

two。 Anti-Corialization

Corialization is a fixed part of the parameters, returning a function that accepts the remaining parameters, also known as the partial calculation function, in order to narrow the scope of application and create a more targeted function. The core idea is to split the multi-parameter function into a single-parameter (or part) function, and then return to call the next single-parameter (or part) function, and deal with the remaining parameters in turn.

On the other hand, anti-Corialization, literally, the meaning and usage are just the opposite of function Corialization, which expands the scope of application and creates a function with a wider range of application. Extend methods that would otherwise be applicable only to specific objects to more objects.

Let's first take a look at the general implementation of anti-Corialization.

/ / ES5 mode Function.prototype.unCurrying = function () {var self = this return function () {var rest = Array.prototype.slice.call (arguments) return Function.prototype.call.apply (self, rest)}} / / ES6 mode Function.prototype.unCurrying = function () {const self = this return function (. Rest) {return Function.prototype.call.apply (self, rest)}}

If you don't think it's a good idea to put the function on the Function prototype, you can do the same:

/ / ES5 mode function unCurrying (fn) {return function (tar) {var rest = Array.prototype.slice.call (arguments) rest.shift () return fn.apply (tar, rest)}} / / ES6 mode function unCurrying (fn) {return function (tar,... argu) {return fn.apply (tar, argu)}}

Let's briefly try the general implementation of anti-Corey. Let's lend the push method on Array to add an element to an array of classes such as arguments:

/ / connect var push = unCurrying (Array.prototype.push) function execPush () {push (arguments, 4) console.log (arguments)} execPush (1,2,3) / / output: [1,2,3,4]

To put it simply, function Corialization is to reduce the order of higher-order functions, narrow the scope of application, and create a more targeted function.

Function (arg1, arg2) / / = > function (arg1) (arg2) function (arg1, arg2, arg3) / / = > function (arg1) (arg2) (arg3) function (arg1, arg2, arg3, arg4) / / = > function (arg1) (arg2) (arg3) (arg4) function (arg1, arg2,..., argn) / / = > function (arg1) (arg2). (argn)

Anti-Corialization, on the other hand, increases the scope of application and makes the use of the method larger. With anti-Corialization, you can lend native methods so that any object has native object methods.

Obj.func (arg1, arg2) / / = > func (obj, arg1, arg2)

The difference between Corification and anti-Corialization can be understood in this way:

Corialization is to pass parameters in advance before operation, and multiple parameters can be passed.

Decolorization is to delay the transfer of parameters, in which the original fixed parameters or this context are delayed as parameters to be passed in the future.

For higher-order usage, see JavaScript functional programming skills-Decorization.

3. Partial function

A partial function is to create a function that calls another part (a function whose parameters or variables have been prefabricated), and the function can generate a function that is actually executed based on the passed parameters. It does not include the logic code we really need, but only returns other functions according to the passed parameters, and the returned functions have real processing logic such as:

Var isType = function (type) {return function (obj) {return Object.prototype.toString.call (obj) = `[object ${type}]`}} var isString = isType ('String') var isFunction = isType (' Function')

In this way, we can quickly create a set of methods to judge the type of object by using partial function.

The difference between partial function and Corialization:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Corylization is to change a function that accepts n parameters from passing all parameters at once and executing them into accepting parameters multiple times, for example: add = (x, y, z) = > x + y + z → curryAdd = x = > y = > z = > x + y + z

The partial function fixes a part of the function and accepts the remaining parameters by returning a new function through the passed parameter or method. The number may be one or more.

When a Corialized function accepts only two parameters, such as curry () (), the concept of the Corialized function is similar to that of the partial function, and the partial function can be considered as a degenerate version of the Corialized function.

After reading the above, have you mastered the methods of JavaScript closures and higher-order functions? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report