日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

深入詳解JS函數的柯里化

瀏覽:155日期:2024-03-23 08:56:24
目錄一、補充知識點之函數的隱式轉換二、補充知識點之利用call/apply封數組的map方法三、由淺入深的柯里化四、柯里化通用式五、柯里化與bind一、補充知識點之函數的隱式轉換

來一個簡單的思考題。

function fn() { return 20;}console.log(fn + 10); // 輸出結果是多少?

稍微修改一下,再想想輸出結果會是什么?

function fn() { return 20;} fn.toString = function() { return 10;} console.log(fn + 10); // 輸出結果是多少?

還可以繼續修改一下。

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

當使用console.log,或者進行運算時,隱式轉換就可能會發生。從上面三個例子中我們可以得出一些關于函數隱式轉換的結論。

當我們沒有重新定義toString與valueOf時,函數的隱式轉換會調用默認的toString方法,它會將函數的定義內容作為字符串返回。而當我們主動定義了toString/vauleOf方法時,那么隱式轉換的返回結果則由我們自己控制了。其中valueOf的優先級會toString高一點。

因此上面例子的結論就很容易理解了。建議大家動手嘗試一下。

二、補充知識點之利用call/apply封數組的map方法

map(): 對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。

通俗來說,就是遍歷數組的每一項元素,并且在map的第一個參數(回調函數)中進行運算處理后返回計算結果。返回一個由所有計算結果組成的新數組。

// 回調函數中有三個參數// 第一個參數表示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]

在上面例子的注釋中詳細闡述了map方法的細節。現在要面臨一個難題,就是如何封裝map。

可以先想想for循環。我們可以使用for循環來實現一個map,但是在封裝的時候,我們會考慮一些問題。我們在使用for循環的時候,一個循環過程確實很好封裝,但是我們在for循環里面要對每一項做的事情卻很難用一個固定的東西去把它封裝起來。因為每一個場景,for循環里對數據的處理肯定都是不一樣的。

于是大家就想了一個很好的辦法,將這些不一樣的操作單獨用一個函數來處理,讓這個函數成為map方法的第一個參數,具體這個回調函數中會是什么樣的操作,則由我們自己在使用時決定。因此,根據這個思路的封裝實現如下。

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]

在上面的封裝中,我首先定義了一個空的temp數組,該數組用來存儲最終的返回結果。在for循環中,每循環一次,就執行一次參數fn函數,fn的參數則使用call方法傳入。

在理解了map的封裝過程之后,我們就能夠明白為什么我們在使用map時,總是期望能夠在第一個回調函數中有一個返回值了。在eslint的規則中,如果我們在使用map時沒有設置一個返回值,就會被判定為錯誤。

ok,明白了函數的隱式轉換規則與call/apply在這種場景的使用方式,我們就可以嘗試通過簡單的例子來了解一下柯里化了。

三、由淺入深的柯里化

在前端面試中有一個關于柯里化的面試題,流傳甚廣。

實現一個add方法,使計算結果能夠滿足如下預期:

add(1)(2)(3) = 6add(1, 2, 3)(4) = 10add(1)(2)(3)(4)(5) = 15

很明顯,計算結果正是所有參數的和,add方法每運行一次,肯定返回了一個同樣的函數,繼續計算剩下的參數。

我們可以從最簡單的例子一步一步尋找解決方案。

當我們只調用兩次時,可以這樣封裝。

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

上面的封裝看上去跟我們想要的結果有點類似,但是參數的使用被限制得很死,因此并不是我們想要的最終結果,我們需要通用的封裝。應該怎么辦?總結一下上面2個例子,其實我們是利用閉包的特性,將所有的參數,集中到最后返回的函數里進行計算并返回結果。因此我們在封裝時,主要的目的,就是將參數集中起來計算。

來看看具體實現。

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)); // 15console.log(add(1, 2, 3, 4)(5)); // 15console.log(add(1)(2)(3)(4)(5)); // 15

上面的實現,利用閉包的特性,主要目的是想通過一些巧妙的方法將所有的參數收集在一個數組里,并在最終隱式轉換時將數組里的所有項加起來。因此我們在調用add方法的時候,參數就顯得非常靈活。當然,也就很輕松的滿足了我們的需求。

那么讀懂了上面的demo,然后我們再來看看柯里化的定義,相信大家就會更加容易理解了。

柯里化(英語:Currying),又稱為部分求值,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回一個新的函數的技術,新函數接受余下參數并返回運算結果。

接收單一參數,因為要攜帶不少信息,因此常常以回調函數的理由來解決。 將部分參數通過回調函數等方式傳入函數中 返回一個新函數,用于處理所有的想要傳入的參數

在上面的例子中,我們可以將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-6console.log(res2); // 0-1-2-3console.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)); // 40console.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的功能。

在前面的幾個例子中,我們可以總結一下柯里化的特點:

接收單一參數,將更多的參數通過回調函數來搞定? 返回一個新函數,用于處理所有的想要傳入的參數; 需要利用call/apply與arguments對象收集參數; 返回的這個函數正是用來處理收集起來的參數。

希望大家讀完之后都能夠大概明白柯里化的概念,如果想要熟練使用它,就需要我們掌握更多的實際經驗才行。

以上就是深入詳解JS函數的柯里化的詳細內容,更多關于JS函數的柯里化的資料請關注好吧啦網其它相關文章!

標簽: JavaScript
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
91精品观看| 亚洲综合小说| 国产精品极品在线观看| 日韩毛片网站| 日韩av一二三| 日韩激情av在线| 日本强好片久久久久久aaa| 日韩一区精品| 国产日产高清欧美一区二区三区| 日韩精品一级| 首页欧美精品中文字幕| 久久亚洲一区| 91麻豆精品| 麻豆国产一区| 欧美成人精品三级网站| 国产免费久久| 久久尤物视频| 日韩高清不卡| 免费视频久久| 91成人在线精品视频| 欧美精品99| 国产+成+人+亚洲欧洲在线| 涩涩av在线| 狠狠爱www人成狠狠爱综合网| 日本欧美在线看| 国产美女久久| 日韩国产欧美一区二区| 亚洲免费成人| 97久久精品| 免费视频一区二区三区在线观看| 国产一区二区三区成人欧美日韩在线观看| 福利在线免费视频| 99日韩精品| 国产亚洲高清一区| 樱桃视频成人在线观看| 午夜在线视频观看日韩17c| 日韩欧美久久| 久久影院午夜精品| 亚洲专区欧美专区| 久久激情五月婷婷| 夜鲁夜鲁夜鲁视频在线播放| 在线精品小视频| 日韩高清一区二区| 成人在线黄色| 亚洲欧美日韩精品一区二区| 青青草国产精品亚洲专区无| 欧美韩日一区| 久久成人一区| 国产精品www994| 激情综合网址| 日本不卡高清| 久久久一本精品| 日韩综合一区二区| 日韩精品1区| 日韩精品福利一区二区三区| 日韩毛片视频| 日本色综合中文字幕| 欧美一区二区三区高清视频| 欧美日韩一区二区高清| 国产一区观看| 欧美激情五月| 亚州av乱码久久精品蜜桃| 国产日韩欧美一区在线| 欧美日韩国产一区二区三区不卡| 国产精品一区二区三区www| 伊人久久大香线蕉av不卡| 欧美久久香蕉| 最新亚洲一区| 国产精品久久观看| 日韩精品国产欧美| 五月婷婷六月综合| 电影91久久久| 国产日韩欧美一区在线| 久久亚洲在线| 欧美aa在线视频| 男人的天堂久久精品| 精品国产午夜肉伦伦影院| 在线精品一区| 999精品在线| 美女视频黄免费的久久| 视频一区免费在线观看| 黄色在线观看www| 清纯唯美亚洲综合一区| 亚洲免费精品| 欧美日韩视频免费观看| 美女在线视频一区| 青青草伊人久久| 狠狠爱www人成狠狠爱综合网| 国产精品久久久久久久免费观看| 国产调教精品| 亚洲不卡视频| 91精品一区国产高清在线gif| 免费在线观看一区| 欧美日本精品| 日韩精品中文字幕吗一区二区 | 91综合网人人| 国产精品分类| 91成人在线| 日韩三级精品| 日韩影院精彩在线| 亚洲激精日韩激精欧美精品| 中文字幕成在线观看| 美女精品一区二区| 91欧美精品| 亚洲精品欧美| 视频在线观看一区| 国产视频一区欧美| 欧美午夜不卡| 国产高清久久| 亚洲一级特黄| 99久久久久久中文字幕一区| 成人免费一区| 国际精品欧美精品| 国产日韩1区| 久久精品99国产精品日本| 日本一区二区三区视频在线看| 国产视频一区三区| 久久国产中文字幕| 精品资源在线| 国产精品99久久久久久董美香| 日韩欧美三区| 国产日本精品| 国产精品久久乐| 久久精品伊人| 91亚洲国产| 91青青国产在线观看精品| 国产一区二区精品福利地址| 国产欧美午夜| 国产欧美一区二区色老头| 久久精品99国产精品| 国产日韩欧美三区| 国产精品亚洲人成在99www| 婷婷综合成人| 亚洲男女自偷自拍| 婷婷综合网站| 午夜精品一区二区三区国产| 久久一区二区中文字幕| 日韩精品网站| 91精品国产乱码久久久久久久| а√在线中文在线新版| 色爱综合网欧美| 日韩成人a**站| 日韩欧美1区| 久久精品高清| 欧美特黄一区| 美女91精品| 亚洲综合福利| 日本不卡一区二区三区| 国产人成精品一区二区三| 国产精品一页| 国产精品毛片久久| 91精品蜜臀一区二区三区在线| 精品一区三区| 免费久久99精品国产| 欧美日韩精品一本二本三本| 久久av在线| 日欧美一区二区| 久久久免费人体| 日韩电影免费在线观看| 亚洲国产一区二区在线观看| 日韩在线播放一区二区| 欧美亚洲色图校园春色| 美女视频黄久久| 日韩激情一区| 久久亚洲电影| 国产日韩欧美一区在线| 国产精品yjizz视频网| 国户精品久久久久久久久久久不卡 | 亚洲美女久久| 国产精品亚洲综合久久| 亚洲伦乱视频| 蜜臀国产一区二区三区在线播放 | 国产一级久久| 欧美亚洲三级| 国产中文欧美日韩在线| 久久久久欧美精品| 亚洲九九精品| 亚洲区国产区| 精品国产乱码久久久久久1区2匹| 亚洲h色精品| 91精品麻豆| 欧美三级精品| 99riav国产精品| 国产情侣久久| 精品在线播放| 久久成人av| 午夜一级在线看亚洲| 国产精品探花在线观看| 日韩国产网站| 日本视频在线一区| 久久久久久色| 香蕉视频亚洲一级| 天堂久久av| 久久精品123| 欧美一区自拍| 精品日韩视频| 日本色综合中文字幕| 丝袜av一区| 国产欧美一区二区色老头| 色综合www|