


Advanced front-end basics (8): In-depth explanation of currying of functions
Currying is a relatively advanced application of functions, and it is not easy to understand it. So I've been thinking about how I should express myself more to make it easier for everyone to understand. After thinking about it for a long time, I decided to put aside the concept of currying and add two important but easily overlooked knowledge points.
1. Supplementary knowledge points of implicit conversion of functions
As a weakly typed language, JavaScript’s implicit conversion is very flexible and interesting. When we do not have a deep understanding of implicit conversions, we may be confused by the results of some operations, such as 4 + true = 5. Of course, if you have a deep enough understanding of implicit conversion, you will definitely be able to greatly improve your ability to use js. It's just that I don't plan to share all the implicit conversion rules with you. Here I will only share some rules for implicit conversion of functions.
Let’s ask a simple question.
function fn() { return 20; } console.log(fn + 10); // 输出结果是多少?
Modify it slightly and think about what the output will be?
function fn() { return 20; } fn.toString = function() { return 10; } console.log(fn + 10); // 输出结果是多少?
You can continue to modify it.
function fn() { return 20; } fn.toString = function() { return 10; } fn.valueOf = function() { return 5; } console.log(fn + 10); // 输出结果是多少?
// 输出结果分别为 function fn() { return 20; }10 20 15
When using console.log, or performing calculations, implicit conversion may occur. From the above three examples we can draw some conclusions about implicit conversion of functions.
When we do not redefine toString and valueOf, the implicit conversion of the function will call the default toString method, which will return the definition content of the function as a string. When we actively define the toString/vauleOf method, the return result of the implicit conversion is controlled by ourselves. The priority of valueOf will be higher than toString.
So the conclusion of the above example is easy to understand. I suggest you give it a try.
## 2. Supplementary knowledge points: the map method of using call/apply to seal the array
// 回调函数中有三个参数 // 第一个参数表示newArr的每一项,第二个参数表示该项在数组中的索引值 // 第三个表示数组本身 // 除此之外,回调函数中的this,当map不存在第二参数时,this指向丢失,当存在第二个参数时,指向改参数所设定的对象 var newArr = [1, 2, 3, 4].map(function(item, i, arr) { console.log(item, i, arr, this); // 可运行试试看 return item + 1; // 每一项加1 }, { a: 1 }) console.log(newArr); // [2, 3, 4, 5]
Array.prototype._map = function(fn, context) { var temp = []; if(typeof fn == 'function') { var k = 0; var len = this.length; // 封装for循环过程 for(; k < len; k++) { // 将每一项的运算操作丢进fn里,利用call方法指定fn的this指向与具体参数 temp.push(fn.call(context, this[k], k, this)) } } else { console.error('TypeError: '+ fn +' is not a function.'); } // 返回每一项运算结果组成的新数组 return temp; } var newArr = [1, 2, 3, 4].map(function(item) { return item + 1; }) // [2, 3, 4, 5]
3. Currying from shallow to deep
add(1)(2)(3) = 6 add(1, 2, 3)(4) = 10 add(1)(2)(3)(4)(5) = 15
function add(a) { return function(b) { return a + b; } } console.log(add(1)(2)); // 3
function add(a) { return function(b) { return function (c) { return a + b + c; } } } console.log(add(1)(2)(3)); // 6
function add() { // 第一次执行时,定义一个数组专门用来存储所有的参数 var _args = [].slice.call(arguments); // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值 var adder = function () { var _adder = function() { [].push.apply(_args, [].slice.call(arguments)); return _adder; }; // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); } return _adder; } return adder.apply(null, [].slice.call(arguments)); } // 输出结果,可自由组合的参数 console.log(add(1, 2, 3, 4, 5)); // 15 console.log(add(1, 2, 3, 4)(5)); // 15 console.log(add(1)(2)(3)(4)(5)); // 15
上面的实现,利用闭包的特性,主要目的是想通过一些巧妙的方法将所有的参数收集在一个数组里,并在最终隐式转换时将数组里的所有项加起来。因此我们在调用add方法的时候,参数就显得非常灵活。当然,也就很轻松的满足了我们的需求。
那么读懂了上面的demo,然后我们再来看看柯里化的定义,相信大家就会更加容易理解了。
柯里化(英语:Currying),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。
1.接收单一参数,因为要携带不少信息,因此常常以回调函数的理由来解决。
2.将部分参数通过回调函数等方式传入函数中
3.返回一个新函数,用于处理所有的想要传入的参数
在上面的例子中,我们可以将add(1, 2, 3, 4)转换为add(1)(2)(3)(4)。这就是部分求值。每次传入的参数都只是我们想要传入的所有参数中的一部分。当然实际应用中,并不会常常这么复杂的去处理参数,很多时候也仅仅只是分成两部分而已。
咱们再来一起思考一个与柯里化相关的问题。
假如有一个计算要求,需要我们将数组里面的每一项用我们自己想要的字符给连起来。我们应该怎么做?想到使用join方法,就很简单。
var arr = [1, 2, 3, 4, 5]; // 实际开发中并不建议直接给Array扩展新的方法 // 只是用这种方式演示能够更加清晰一点 Array.prototype.merge = function(chars) { return this.join(chars); } var string = arr.merge('-') console.log(string); // 1-2-3-4-5
增加难度,将每一项加一个数后再连起来。那么这里就需要map来帮助我们对每一项进行特殊的运算处理,生成新的数组然后用字符连接起来了。实现如下:
var arr = [1, 2, 3, 4, 5]; Array.prototype.merge = function(chars, number) { return this.map(function(item) { return item + number; }).join(chars); } var string = arr.merge('-', 1); console.log(string); // 2-3-4-5-6
但是如果我们又想要让数组每一项都减去一个数组之后再连起来呢?当然和上面的加法操作一样的实现。
var arr = [1, 2, 3, 4, 5]; Array.prototype.merge = function(chars, number) { return this.map(function(item) { return item - number; }).join(chars); } var string = arr.merge('~', 1); console.log(string); // 0~1~2~3~4
机智的小伙伴肯定发现困惑所在了。我们期望封装一个函数,能同时处理不同的运算过程,但是我们并不能使用一个固定的套路将对每一项的操作都封装起来。于是问题就变成了和封装map的时候所面临的问题一样了。我们可以借助柯里化来搞定。
与map封装同样的道理,既然我们事先并不确定我们将要对每一项数据进行怎么样的处理,我只是知道我们需要将他们处理之后然后用字符连起来,所以不妨将处理内容保存在一个函数里。而仅仅固定封装连起来的这一部分需求。
于是我们就有了以下的封装。
// 封装很简单,一句话搞定 Array.prototype.merge = function(fn, chars) { return this.map(fn).join(chars); } var arr = [1, 2, 3, 4]; // 难点在于,在实际使用的时候,操作怎么来定义,利用闭包保存于传递num参数 var add = function(num) { return function(item) { return item + num; } } var red = function(num) { return function(item) { return item - num; } } // 每一项加2后合并 var res1 = arr.merge(add(2), '-'); // 每一项减2后合并 var res2 = arr.merge(red(1), '-'); // 也可以直接使用回调函数,每一项乘2后合并 var res3 = arr.merge((function(num) { return function(item) { return item * num } })(2), '-') console.log(res1); // 3-4-5-6 console.log(res2); // 0-1-2-3 console.log(res3); // 2-4-6-8
大家能从上面的例子,发现柯里化的特征吗?
四、柯里化通用式
通用的柯里化写法其实比我们上边封装的add方法要简单许多。
var currying = function(fn) { var args = [].slice.call(arguments, 1); return function() { // 主要还是收集所有需要的参数到一个数组中,便于统一计算 var _args = args.concat([].slice.call(arguments)); return fn.apply(null, _args); } } var sum = currying(function() { var args = [].slice.call(arguments); return args.reduce(function(a, b) { return a + b; }) }, 10) console.log(sum(20, 10)); // 40 console.log(sum(10, 5)); // 25
五、柯里化与bind
Object.prototype.bind = function(context) { var _this = this; var args = [].prototype.slice.call(arguments, 1); return function() { return _this.apply(context, args) } }
这个例子利用call与apply的灵活运用,实现了bind的功能。
在前面的几个例子中,我们可以总结一下柯里化的特点:
1.接收单一参数,将更多的参数通过回调函数来搞定?
2.返回一个新函数,用于处理所有的想要传入的参数;
3.需要利用call/apply与arguments对象收集参数;
4.返回的这个函数正是用来处理收集起来的参数。
希望大家读完之后都能够大概明白柯里化的概念,如果想要熟练使用它,就需要我们掌握更多的实际经验才行。

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



PHP and Vue: a perfect pairing of front-end development tools. In today's era of rapid development of the Internet, front-end development has become increasingly important. As users have higher and higher requirements for the experience of websites and applications, front-end developers need to use more efficient and flexible tools to create responsive and interactive interfaces. As two important technologies in the field of front-end development, PHP and Vue.js can be regarded as perfect tools when paired together. This article will explore the combination of PHP and Vue, as well as detailed code examples to help readers better understand and apply these two

How to use PHP arrow functions to implement currying of functions Currying (Currying) is a functional programming concept, which refers to the process of converting a multi-parameter function into a function sequence that only accepts a single parameter. In PHP, we can use arrow functions to implement currying of functions, making the code more concise and flexible. The so-called arrow function is a new anonymous function syntax introduced in PHP7.4. Its characteristic is that it can capture external variables and has only one expression as the function body.

In front-end development interviews, common questions cover a wide range of topics, including HTML/CSS basics, JavaScript basics, frameworks and libraries, project experience, algorithms and data structures, performance optimization, cross-domain requests, front-end engineering, design patterns, and new technologies and trends. . Interviewer questions are designed to assess the candidate's technical skills, project experience, and understanding of industry trends. Therefore, candidates should be fully prepared in these areas to demonstrate their abilities and expertise.

As a C# developer, our development work usually includes front-end and back-end development. As technology develops and the complexity of projects increases, the collaborative development of front-end and back-end has become more and more important and complex. This article will share some front-end and back-end collaborative development techniques to help C# developers complete development work more efficiently. After determining the interface specifications, collaborative development of the front-end and back-end is inseparable from the interaction of API interfaces. To ensure the smooth progress of front-end and back-end collaborative development, the most important thing is to define good interface specifications. Interface specification involves the name of the interface

Django is a web application framework written in Python that emphasizes rapid development and clean methods. Although Django is a web framework, to answer the question whether Django is a front-end or a back-end, you need to have a deep understanding of the concepts of front-end and back-end. The front end refers to the interface that users directly interact with, and the back end refers to server-side programs. They interact with data through the HTTP protocol. When the front-end and back-end are separated, the front-end and back-end programs can be developed independently to implement business logic and interactive effects respectively, and data exchange.

As a fast and efficient programming language, Go language is widely popular in the field of back-end development. However, few people associate Go language with front-end development. In fact, using Go language for front-end development can not only improve efficiency, but also bring new horizons to developers. This article will explore the possibility of using the Go language for front-end development and provide specific code examples to help readers better understand this area. In traditional front-end development, JavaScript, HTML, and CSS are often used to build user interfaces

Methods for implementing instant messaging include WebSocket, Long Polling, Server-Sent Events, WebRTC, etc. Detailed introduction: 1. WebSocket, which can establish a persistent connection between the client and the server to achieve real-time two-way communication. The front end can use the WebSocket API to create a WebSocket connection and achieve instant messaging by sending and receiving messages; 2. Long Polling, a technology that simulates real-time communication, etc.

Django: A magical framework that can handle both front-end and back-end development! Django is an efficient and scalable web application framework. It is able to support multiple web development models, including MVC and MTV, and can easily develop high-quality web applications. Django not only supports back-end development, but can also quickly build front-end interfaces and achieve flexible view display through template language. Django combines front-end development and back-end development into a seamless integration, so developers don’t have to specialize in learning
