函數語言程式設計3-作用域與閉包

瘋狂的小蘑菇發表於2019-01-27

動態作用域

在任何JS核心引擎中,都有一張全域性表來儲存全域性變數

const _ = require(`../util/understore`)

// 動態作用域,任何JS核心引擎中,有個全域性查詢表
const globals = {}

const makeBind = resolver => (name, val) => {
  const stack = globals[name] || []
  globals[name] = resolver(stack, val)
  return globals
}

const stackBinder = makeBind((stack, val) => {
  stack.push(val)
  return stack
})

const stackUnBinder = makeBind((stack) => {
  stack.pop()
  return stack
})

const dynmicLookup = (name) => {
  const slot = globals[name] || []
  return _.last(slot)
}

stackBinder(`a`, 1)
stackBinder(`a`, 2)
stackBinder(`b`, 100)
console.log(dynmicLookup(`a`))
stackUnBinder(`a`)
console.log(dynmicLookup(`a`))

const f = () => {
  return dynmicLookup(`a`)
}

const g = () => {
  stackBinder(`a`, `g`)
  return f()
}

console.log(f())
// 動態作用域的缺點,任何繫結值,在確定呼叫函式之前,都是不可知的。
console.log(g())複製程式碼

繫結

我們無法精確控制函式中的this是什麼,因為函式的this由呼叫者決定。
所以,我們需要在呼叫函式前進行繫結。

const bindAll = (obj, ...funs) => {
  return funs.map(fun => obj[fun] = obj[fun].bind(obj))
}

const target = {
  name: `name`,
  a: function () {
    return this.name
  },
  b: function () {
    return this.a()
  },
}
console.log(target.b())

// 以下方式會報錯
// console.log(target.b.call(`lhj`))

// 使用bindAll解決這個問題,預繫結
bindAll(target, `a`, `b`)

console.log(target.b.call(`lhj`))複製程式碼

閉包

閉包的用途非常廣泛,任何函式內部能讀取外部變數(包括引數,全域性變數,私有變數),這就形成了一個閉包。

// 求平均數
const average = array => array.reduce((prev, next) => prev + next) / array.length

// 求指定數字與通過函式生成另外一個數字
const averageDynmic = fun => n => average([n].concat(fun(n)))

console.log(averageDynmic(n => ([n * n]))(10))

// 求反義函式
const complement = fun => (...args) => !fun(...args)

const isOdd = n => n % 2 === 0

const isEven = complement(isOdd)

console.log(isOdd(1), isOdd(2))
console.log(isEven(1), isEven(2))

// 封裝資料
const Team = () => {
  let num = 1
  const people = {
    lhj: {
      age: 30,
    },
  }
  return {
    add: n => num += n,
    del: n => num -= n,
    update: (name, value) => people[name] = value,
  }
}

const t1 = Team()
console.log(t1.add(3))
console.log(t1.del(2))

console.log(t1.update(`lhj`, {
  age: 31,
}))複製程式碼

相關文章