ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript の高階関数の概要 (コード例)

JavaScript の高階関数の概要 (コード例)

不言
不言転載
2019-03-05 14:51:252333ブラウズ

この記事では、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 操作は非常にコストがかかります。そのようなシナリオの一部を以下に示します。

  • window.resise イベント。
  • マウス、入力、その他のイベント。
  • アップロードの進捗状況

# 高階関数による関数スロットリングを実装しましょう

/**
* 节流函数
* @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(&#39;throttle&#39;)
},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(&#39;p&#39;)
 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(&#39;eventLazy&#39;), &#39;click&#39;, function() {
 console.log(&#39;lazy &#39;)
})

ブランチに入ると、関数の実装は関数内で変更されます。書き換えると、関数は期待する関数になり、次の関数には存在しなくなります。関数に入るときの条件分岐ステートメント。

最後に

#皆さんが学習をより簡単かつ効率的に行えるよう、完全なレベルに達するのに役立つ大量の情報を無料で共有します。スタック エンジニアやアーキテクトであっても、道路には多くの障害物があります。ここでは、フロントエンドのフルスタックの学習および交換サークルを全員にお勧めします: 866109386. 全員がグループに参加して、一緒に議論し、学習し、進歩することを歓迎します。

実際に学習を始めると、どこから始めればよいのか必然的に分からなくなり、効率が低下し、学習を続ける自信に影響を及ぼします。

しかし、最も重要なことは、どのテクノロジーを習得する必要があるのか​​が分からず、学習中に頻繁に落とし穴に足を踏み入れてしまい、最終的には多くの時間を無駄にしてしまうということです。リソース。

最後に、ボトルネックに遭遇し、何をすればよいのか分からないすべてのフロントエンド プログラマーの皆様、今後の仕事や面接でのご多幸をお祈り申し上げます。


以上がJavaScript の高階関数の概要 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。