この記事では、JavaScript の高階関数 (コード例) について紹介しますが、これには一定の参考価値があります。参考になれば幸いです。
関数は別の関数をパラメータとして受け取ることができます。つまり、関数のパラメータは他の関数を受け取ることができます。この種の関数は高階関数と呼ばれます。
高階JavaScript 関数は、Swift の高階関数に似ています。
一般的な高階関数には、Map、Reduce、Filter、Sort が含まれます。
高階関数とは、少なくとも次の条件を満たす関数を指します。次のいずれかの条件
1: 関数をパラメータとして渡すことができる
2: 関数を戻り値として出力できる
JavaScript 言語の関数高階関数の条件を明らかに満たす、JavaScript の高階関数の魅力を探ってみましょう。
高階関数による AOP の実装
AOP (アスペクト指向プログラミング) の主な機能は、コアのビジネス ロジックと関係のないいくつかの関数を抽出することです。ビジネスモジュールには「ダイナミックウィービング」メソッドが組み込まれています。これらの機能には通常、ログ統計、セキュリティ制御、例外処理などが含まれます。 AOP は Java Spring アーキテクチャの中核です。 JavaScript で AOP を実装する方法を見てみましょう
JavaScript での AOP の実装とは、関数を別の関数に「動的に織り込む」ことを指します。特定の実装テクノロジが多数あります。これを行うには Function.prototype を使用します。コードは次のとおりです。
/** * 织入执行前函数 * @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)
カリー化について最初に説明する必要があるのは、関数のカリー化とは何かということです。
カリングは部分評価とも呼ばれます。 curring 関数は最初にいくつかのパラメータを受け入れます。これらのパラメータを受け入れた後、関数はすぐには評価されませんが、引き続き別の関数を返します。渡されたばかりのパラメータは、関数によって形成されたクロージャに保存されます。関数を実際に評価する必要がある場合、前に渡されたすべてのパラメータが一度に評価に使用されます。
概念を厳密に見ると理解するのは簡単ではありません。次の例を見てみましょう。
1 年のうち 12 か月分の消費量を計算する関数が必要です。次の時点で計算する必要があります。毎月末にどれくらいのお金が使われたか。通常のコードは次のとおりです。
// 未柯里化的函数计算开销 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个月的花销
要約すると、1 年間の総消費量を計算したい場合、12 回計算する必要がないことがわかります。年末に実行する必要がある計算は 1 つだけです。次に、理解を助けるためにこの関数を部分的にカリー化します。
// 部分柯里化完的函数 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()
次に、一般的なカリー化と修復される関数を作成します。コードは次のとおりです
// 通用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()
関数スロットリング
JavaScript のほとんどの関数は、ユーザーによってアクティブにトリガーされます。通常、パフォーマンスの問題はありませんが、特殊な場合にはユーザーによって直接制御されないことがあります。多数の呼び出しにより、パフォーマンスの問題が発生しやすくなります。結局のところ、DOM 操作は非常にコストがかかります。そのようなシナリオの一部を以下に示します。
# 高階関数による関数スロットリングを実装しましょう
/** * 节流函数 * @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)
タイムシェアリング関数
スロットル関数は、呼び出される関数の頻度を制限するソリューションを提供します。次に、別の問題が発生します。一部の関数はユーザーによって積極的に呼び出されますが、いくつかの客観的な理由により、これらの操作はページのパフォーマンスに重大な影響を及ぼします。現時点では、別の方法を使用して問題を解決する必要があります。
短期間に多数の DOM ノードをページに挿入する必要がある場合、明らかにブラウザーにとって多大な負荷がかかります。ブラウザがフリーズする可能性があるため、タイムシェアリング機能を実行し、バッチで挿入する必要があります。
/** * 分时函数 * @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()
遅延読み込み機能
Web開発では、一部のブラウザの違いにより、スニッフィング作業が常に避けられません。
ブラウザーの違いにより、さまざまな互換性の改善が必要になることがよくありますが、非常にシンプルでよく使用される例として、さまざまなブラウザーで共通に使用できるイベント バインディング関数を考えてみましょう。
一般的な書き方は次のようになります:
// 常用的事件兼容 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) } } })()
この方法では、コードがロードされる前にスニッフィングしてから関数を返すことができます。しかし、公共図書館に置いて利用しないのであれば、少し冗長になります。次に、この問題を解決するために遅延関数を使用します:
// 惰性加载函数 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 ') })
ブランチに入ると、関数の実装は関数内で変更されます。書き換えると、関数は期待する関数になり、次の関数には存在しなくなります。関数に入るときの条件分岐ステートメント。
最後に
#皆さんが学習をより簡単かつ効率的に行えるよう、完全なレベルに達するのに役立つ大量の情報を無料で共有します。スタック エンジニアやアーキテクトであっても、道路には多くの障害物があります。ここでは、フロントエンドのフルスタックの学習および交換サークルを全員にお勧めします: 866109386. 全員がグループに参加して、一緒に議論し、学習し、進歩することを歓迎します。
実際に学習を始めると、どこから始めればよいのか必然的に分からなくなり、効率が低下し、学習を続ける自信に影響を及ぼします。
しかし、最も重要なことは、どのテクノロジーを習得する必要があるのかが分からず、学習中に頻繁に落とし穴に足を踏み入れてしまい、最終的には多くの時間を無駄にしてしまうということです。リソース。
最後に、ボトルネックに遭遇し、何をすればよいのか分からないすべてのフロントエンド プログラマーの皆様、今後の仕事や面接でのご多幸をお祈り申し上げます。
以上がJavaScript の高階関数の概要 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。