博客 / 詳情

返回

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 #<Object>

var user = { name: 'alice' }
Object.defineProperty(user, 'address', {
  value: '上海'
})
delete user.address

4、嚴格模式不允許函數參數有相同的名稱
非嚴格模式下執行函數,最終輸出3,2,3,嚴格模式下拋出異常 SyntaxError: Duplicate parameter name not allowed in this context

"use strict"
function foo(a, b, a) {
  console.log(a, b, a)
}
foo(1, 2, 3)

5、不允許0的八進制語法
非嚴格模式下,0開頭的數字可代表八進制,嚴格模式下,定義0開頭的數字會拋出異常。Octal literals are not allowed in strict mode.
嚴格模式下可以別的方式定義進制,如 0o(八進制)、0x(十六進制),0b(二進制)

var num = 0212
console.log(num)

6、嚴格模式下,不允許使用with
非嚴格模式下,輸出gloabl,嚴格模式下,直接拋出異常 Strict mode code may not include a with statement

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

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

7、在嚴格模式下,eval不再為上層引用變量
非嚴格模式下,eval執行的字符串中定義變量,變量將被添加到全局,而嚴格模式下,不會被添加。
以下代碼非嚴格模式下,會輸出兩次global,嚴格模式下,輸出一個global,外加拋出異常 message is not defined

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

8、嚴格模式下,this綁定不會默認轉成對象
獨立函數調用時,非嚴格模式下,this指向window,嚴格模式下,this為不會默認指向window。
以下代碼在非嚴格模式下,輸出window對象,嚴格模式下,輸出undefined

function foo(){
  console.log(this)
}
foo()

以上純函數、柯里化、組合函數、with語句、eval、嚴格模式都是函數式編程中的概念,關於js高級,還有很多需要開發者掌握的地方,可以看看我寫的其他博文,持續更新中~

user avatar landejin 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.