js中的函數式編程


函數是javascript中非常重要的一部分,用途也非常的多,可作為參數、返回值、回調等等,下面有一些函數式編程的重要概念和定義

純函數

純函數屬於程序設計的名詞,其它語言中也是存在的,而在javascript中,符合以下規則即為純函數。

  • 函數有相同的輸入,必定有相同的輸出
  • 函數的輸出僅與輸入有關,與其他隱藏信息無關
  • 不得產生任何的副作用,如 觸發事件等

副作用:除了返回函數值以外,還對調用函數產生了其他的影響,如修改全局變量、修改參數或者改變外部存儲。

以下幾個示例來區分一下純函數和非純函數

1、數組方法

var arr1 = [1, 2, 3, 4]
var arr2 = [10, 20, 30, 40]

const newArr1 = arr1.splice(1, 3)
const newArr2 = arr2.slice(1, 3)

console.log(arr1, newArr1) // [1]  [2, 3, 4]
console.log(arr2, newArr2) // [10, 20, 30, 40] [20, 30]

splice 通過下標值和長度操作原數組本身,修改了入參,所以不是純函數。
slice 通過下標值截取數組,返回一個新的數組,沒有副作用,是純函數。

2、修改入參

var obj = { name: 'alice' }
function foo(info) {
  info.name = 'kiki'
  return info
}
foo(obj)

function bar(info) {
  return {
    ...info,
    name: 'kiki'
  }
}
bar(obj)

上面兩個函數的作用都是返回一個新的對象,但foo修改了入參,所以foo不是純函數,bar沒有產生任何副作用,滿足純函數的定義。

使用純函數在開發當中具有一些優點

  • 不用考慮傳入的參數是否已經發生了變化
  • 不用考慮函數的操作是否會修改全局、外部的一些變量

這樣函數的功能就更加的單一和純凈,可以放心的編寫和使用

柯里化

通過函數返回函數的方式,實現多次接收參數並進行統一處理的函數編碼形式

function sum(a, b, c) {
  return a + b + c
}

// 柯里化寫法
function add(a) {
  return function (b) {
    return function (c) {
      return a + b + c
    }
  }
}
sum(1, 2, 3)
add(1)(2)(3)

以上兩個函數的執行結果相同,但是調用方式不同,使用柯里化的優點在於使每一個函數的功能更加單一,及邏輯復用

function foo(type, time, message){
  console.log(`${type}:${[time]}:${message}`)
}
foo('error', '2021/10/24', '接口返回數據異常')
foo('error', new Date(), '頁面顯示錯誤')

// 柯里化寫法
function log(type) {
  return function (time) {
    return function (message) {
      console.log(`${type}:${[time]}:${message}`)
    }
  }
}

var error = log('error')
error('2021/10/24')('接口返回數據異常')
error(new Date())('頁面顯示錯誤')

如以上柯里化代碼,復用了【獲取類型】部分的函數

組合函數

組合函數不是一種函數類型,只是創建一個新函數將多種功能的函數合並起來

function mul(m) {
  return m * 2
}

function square(n) {
  return n * n
}

// 組合函數
function compose(m, n) {
  return function (i) {
    return m(n(i))
  }
}

var fn = compose(mul, square)
console.log(fn(5))

通過組合函數,就可以將不同功能的函數組合使用

with語句

with語句可以創建一個獨立的作用域

with語句的語法是 with(){},with小括號內需要傳遞一個值,一般為對象,with語句內的變量會從這個小括號里先查找

var message = 'global'
var obj = { name: 'alice', message: 'obj' }

function foo() {
  console.log(message)
  with (obj) {
    console.log(message)
  }
}
foo()

以上代碼的執行結果如下

目前with語句已經不推薦使用了

eval

eval用於解析字符串及執行代碼

var str = "var message = 'global'; console.log(message)"
eval(str)

以上代碼執行完成會在控制台上輸出 global

目前已經不推薦使用eval,從上述代碼,我們可以看出它存在的問題

  • 可讀性非常差,也不便於維護
  • eval字符串很可能會在執行的過程中被篡改,容易遭到攻擊
  • js代碼在解析的過程中可以經過js引擎優化,如果是字符串,就沒辦法優化

嚴格模式

在全局或者函數中通過 "use strict" 來開啟嚴格模式,嚴格模式下,不允許編寫一些松散的代碼,瀏覽器會對代碼進行更嚴格的檢測

嚴格模式對javascript語義做了一些限制

1、嚴格模式通過【拋出錯誤】來消除一些原有的【靜默】錯誤
靜默錯誤:比如定義變量沒有關鍵字聲明,如 num = 1

2、嚴格模式在js引擎在執行代碼的時候可以進行更多的優化(不需要對特殊的語法進行處理)
如以下代碼,通過屬性描述符定義name屬性為不可修改

var obj = {}
Object.defineProperty(obj, "name", { writable: false })
obj.name = "alice"

非嚴格模式直接忽略賦值,嚴格模式下會報錯

3、嚴格模式禁用了在ECMAScript未來版本可能會定義的一些語法
如:
關鍵字 function var new
保留字 class let const (ES5以前)
保留字在未來有可能升級為關鍵字

非嚴格模式下以前可以使用保留字作為變量如var let = "abc"
嚴格模式下會報錯

具體來說,嚴格模式做了以下限制
1、無法意外的創建全局變量
在非嚴格模式下,沒有通過關鍵字定義的變量會被添加到全局
嚴格模式下這樣的代碼執行會拋出異常 message is not defined

 message = "hello"

2、嚴格模式會使引起靜默失敗的賦值操作拋出異常
靜默失敗:silently fail,不報錯也沒有任何效果
非嚴格模式下不報錯,嚴格模式下直接拋出異常 Cannot create property 'message' on boolean 'true'

true.name = "alice"

3、嚴格模式下刪除不可刪除的屬性會拋出異常
非嚴格模式下不報錯,忽略刪除操作,嚴格模式下直接拋出異常 Cannot delete property 'address' of #


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM