【DailyENJS第二期】理解JavaScript中的高階函數

【DailyENJS第二期】理解JavaScript中的高階函數

DailyENJS 致力於翻譯優秀的前端英文技術文章,爲技術同窗帶來更好的技術視野。javascript

若是你正在學習JavaScript,那麼你確定遇到太高階函數這個術語。雖然聽起來很複雜,但事實並不是如此。前端

使JavaScript適合函數式編程的緣由是它擁有高階函數。java

高階函數在JavaScript中被普遍使用。若是你已經使用了 JavaScript 一段時間,你可能不知不覺已經使用了它們。要徹底理解這個概念,首先必須瞭解函數式編程是什麼以及 First-Class Functions 的概念。編程

函數式編程是什麼

在大多數簡單的術語中,函數式編程是一種編程形式,你能夠將函數做爲參數傳遞給其餘函數,而且也能夠將它們做爲值返回。在函數式編程中,咱們以函數思惟進行思考和寫代碼。數組

JavaScript,Haskell,Clojure,Scala和Erlang都是實現函數式編程的語言。編程語言

First-Class Functions

若是你一直在學習JavaScript,你可能據說過JavaScript將函數視爲一等公民。那是由於在JavaScript或任何其餘函數式編程語言中,函數都是對象。函數式編程

在JavaScript中,函數是一種特殊類型的對象。它們是Function對象。例如:函數

function greeting() {
  console.log('Hello World');
}
// Invoking the function
greeting();  // prints 'Hello World'
複製代碼

爲了證實函數是JavaScript中的對象,咱們能夠這麼作:學習

// We can add properties to functions like we do with objects
greeting.lang = 'English';
// Prints 'English'
console.log(greeting.lang)
複製代碼

注意: 雖然這在JavaScript中徹底OK,但這被認爲是一種有害的作法。你不該該向函數對象添加隨機屬性,若是必須添加屬性,請使用對象。ui

在JavaScript中,你可使用使用函數就像使用其餘類型(如對象,字符串或數字)同樣。你能夠將它們做爲參數傳遞給其餘函數(回調),將它們分配給變量並傳遞它們等等。這就是JavaScript中的函數被稱爲一等公民的緣由。

將函數賦值給變量

咱們能夠在JavaScript中將函數賦值給變量,例如:

const square = function(x) {
  return x * x;
}
// prints 25
square(5);
複製代碼

咱們也能夠傳遞它們。例如:

const foo = square;
// prints 36
foo(6);
複製代碼

將函數做爲參數傳遞

咱們能夠將函數做爲參數傳遞給其餘函數。例如:

function formalGreeting() {
  console.log("How are you?");
}
function casualGreeting() {
  console.log("What's up?");
}
function greet(type, greetFormal, greetCasual) {
  if(type === 'formal') {
    greetFormal();
  } else if(type === 'casual') {
    greetCasual();
  }
}
// prints 'What's up?'
greet('casual', formalGreeting, casualGreeting);
複製代碼

如今咱們知道了什麼是 first-class functions ,讓咱們深刻了解JavaScript中的高階函數。

高階函數

高階函數是對其餘函數進行操做的函數,能夠將函數做爲參數傳遞或者返回一個函數。簡單來講,高階函數是一個函數,它接收函數做爲參數或將函數做爲輸出進行返回。

例如,Array.prototype.mapArray.prototype.filterArray.prototype.reduce 是語言中內置的一些高階函數。

實踐高階函數

讓咱們看一下內置高階函數的一些例子,看看它與咱們不使用高階函數的解決方案的比較。

Array.prototype.map

map() 方法經過調用一個回調函數來建立一個新數組。map() 方法將從回調函數中獲取每一個返回的值,並使用這些值建立一個新數組。

map()方法的回調函數接受3個參數:elementindexarray

來看一些例子:

例子1

假設咱們有一個數組,咱們想要建立一個新數組,這個數組的值是第一個數組的每一個值的兩倍。讓咱們看看如何使用和不使用高階函數來解決問題:

不使用高階函數:

const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);
複製代碼

使用高階函數:

const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
  return item * 2;
});
console.log(arr2);
複製代碼

咱們甚至可使用箭頭函數使其變得更加簡潔:

const arr1 = [1, 2, 3];
const arr2 = arr1.map(item => item * 2);
console.log(arr2);
複製代碼
例子2

假設咱們有一個包含不一樣人的出生年份的數組,咱們想要建立一個包含其年齡的數組。例如:

不使用高階函數:

const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = [];
for(let i = 0; i < birthYear.length; i++) {
  let age = 2018 - birthYear[i];
  ages.push(age);
}
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);
複製代碼

使用高階函數:

const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = birthYear.map(year => 2018 - year);
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);
複製代碼

Array.prototype.filter

filter() 方法建立了一個經過函數過濾操做後組成的新數組,filter() 的回調函數接受三個參數:element, indexarray

來看一些例子:

例子1

假設咱們有一個包含名稱和年齡屬性的對象的數組。咱們想要建立一個僅包含成年人(年齡大於或等於18)的數組。

不使用高階函數:

const persons = [
  { name: 'Peter', age: 16 },
  { name: 'Mark', age: 18 },
  { name: 'John', age: 27 },
  { name: 'Jane', age: 14 },
  { name: 'Tony', age: 24},
];
const fullAge = [];
for(let i = 0; i < persons.length; i++) {
  if(persons[i].age >= 18) {
    fullAge.push(persons[i]);
  }
}
console.log(fullAge);

複製代碼

使用高階函數:

const persons = [
  { name: 'Peter', age: 16 },
  { name: 'Mark', age: 18 },
  { name: 'John', age: 27 },
  { name: 'Jane', age: 14 },
  { name: 'Tony', age: 24},
];
const fullAge = persons.filter(person => person.age >= 18);
console.log(fullAge)
複製代碼

Array.prototype.reduce

reduce 方法對調用數組的每一個成員執行回調函數,產生一個輸出值。reduce方法接受兩個參數:1)reducer函數(回調),2)和一個可選的 initialValue

reducer 函數(回調)接受四個參數:accumulatorcurrentValuecurrentIndexsourceArray

若是提供了 initialValue,則累加器將等於initialValue,currentValue將等於數組中的第一個元素。

若是沒有提供initialValue,則累加器將等於數組中的第一個元素,currentValue將等於數組中的第二個元素。

例子1

假設咱們必須累加一組數字獲得和:

使用高階函數:

const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
});
// prints 25
console.log(sum);
複製代碼

每次在數組中的每一個值上調用reducer函數時,累加器都會保留reducer函數返回的先前操做的結果,並將currentValue設置爲數組的當前值。最後,結果存儲在sum變量中。

咱們還能夠爲此函數提供初始值:

const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 10);
// prints 35
console.log(sum);
複製代碼

不使用高階函數:

const arr = [5, 7, 1, 8, 4];
let sum = 0;
for(let i = 0; i < arr.length; i++) {
  sum = sum + arr[i];
}
// prints 25
console.log(sum);
複製代碼

你能夠看到使用高階函數使咱們的代碼更清晰,更簡潔,更簡單。

建立咱們本身的高階函數

到目前爲止,咱們看到了語言中內置的各類高階函數。如今讓咱們建立本身的高階函數。

咱們假設JavaScript沒有原生 map 方法。咱們能夠本身建立它,從而建立咱們本身的高階函數。

假設咱們有一個字符串數組,咱們但願將此數組轉換爲整數數組,其中每一個元素表示原始數組中字符串的長度。

const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C'];
function mapForEach(arr, fn) {
  const newArray = [];
  for(let i = 0; i < arr.length; i++) {
    newArray.push(
      fn(arr[i])
    );
  }
  return newArray;
}
const lenArray = mapForEach(strArray, function(item) {
  return item.length;
});
// prints [ 10, 6, 3, 4, 1 ]
console.log(lenArray);
複製代碼

在上面的例子中,咱們建立了一個高階函數 mapForEach,它接受一個數組和一個回調函數fn。

回調函數 fn 接收數組的當前元素並返回該元素的長度,該元素存儲在newArray中。for 循環完成後,返回 newArray 並將其賦值給 lenArray

結論

咱們已經瞭解了高階函數和一些內置的高階函數。咱們還學習瞭如何建立本身的高階函數。簡而言之,高階函數是一個函數,能夠接收函數做爲參數,甚至能夠返回一個函數。高階函數就像常規函數同樣,具備接收和返回其餘函數的額外能力,即參數和輸出。

原文: blog.bitsrc.io/understandi…

相關文章
相關標籤/搜索