使用DOM操作时的纯函数

时间:2017-05-22 09:27:42

标签: javascript dom canvas functional-programming

我试图围绕纯粹的功能,但我不确定我是否真的理解它。我知道纯函数不应该改变外部状态,并且每次都应该返回相同的输出,只要它具有相同的输入。

我知道例如这个函数是不纯的,因为它改变了程序其他部分可能使用的cart变量:

const addToCart = (cart, item) => { 
  cart.push(item); 
  return cart; 
};

纯态的相同功能:

const addToCart = (cart, item) => { 
  const newCart = lodash.cloneDeep(cart); 
  newCart.push(item); 
  return newCart; 
};

这对我有意义。我已经知道纯函数应该总是返回一些东西。

但是,我正在研究一些需要我使用HTML5 canvas元素的东西,而且我有这个函数可以清除画布:

function clearCanvas(canvas) { 
  canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); 
}

如何使上述功能纯净?我意识到它不纯,因为它不会返回任何东西,也会改变画布变量的状态。 DOM操作本质上是不纯的吗?

任何帮助将不胜感激:)

3 个答案:

答案 0 :(得分:2)

DOM操纵是不纯的。你无法以“纯粹”的方式清除画布。

任何改变系统状态或与外界交互的东西据说都有side effects,在纯功能编程环境中,应避免副作用。

但是,DOM操作显然可以做到这两点;清除画布既是状态的改变(未清除也是清除),用户可以看到这种改变。

您可能希望深入了解functional programming,这似乎是您尝试使用纯/不纯函数方法实现的目标。

函数式编程的目的是避免改变状态,例如,一个程序在另一个程序使用它时不会更改对象,这可能会产生意外和不需要的结果。

答案 1 :(得分:2)

IO monad

你可能对IO monad感兴趣 - 本质上IO包含 thunk 或懒惰函数,它只在我们调用runIO时运行。更重要的是,我们可以将事物置于IO中,map是一个普通的函数,允许我们对所包含的值进行操作。

有关良好的阅读和其他IO实施,请参阅Brian Lonsdorf的书中的Chapter 9: Monadic Onions

小演示

// IO :: (void -> a) -> IO a
const IO = f => ({
  // runIO :: void -> a
  runIO: f,
  // map :: IO a => (a -> b) -> IO b
  map: g =>
    IO(() => g(f())),
  // chain :: IO a => (a -> IO b) -> IO b
  chain: g =>
    IO(g(f()).runIO)
})

// IO.of :: a -> IO a
IO.of = x => IO(() => x)

// log :: String -> a -> IO a
const log = label => x =>
  IO(() => (console.log(label, x), x))

// $ :: String -> IO HTMLElement
const $ = x =>
  IO(() => document.querySelector(x))

// getText :: HTMLElement -> String
const getText = e =>
  e.textContent

// main :: String -> IO String
const main = selector =>
  $(selector)
    .map(getText)
    .chain(log('A'))
    .map(s => s.toUpperCase())
    .chain(log('B'))
    .runIO()

main('title')
// A hello world
// B HELLO WORLD
<title>hello world</title>

答案 2 :(得分:0)

通过函数编程范例

来实现
function clearCanvas(canvas) { 
    const imgData = canvas.getContext('2d').getImageData(0,0,canvas.width,canvas.height);
    new Uint32Array(imgData.data.buffer).set(0);
    return data;
} 

canvas.getContext('2d').setImageData(clearCanvas(canvas),0,0);

没有副作用,并为相同的参数返回相同的数据。

但功能性编程是一种防御性的风格,它假设代码是危险的并且危险必须被隔离。因此,它非常耗费资源并且速度慢,并且根本不适合高性能代码。