Javascript 尝试将函数转换为更实用的样式

Javascript 尝试将函数转换为更实用的样式,javascript,functional-programming,refactoring,Javascript,Functional Programming,Refactoring,我正在学习函数式编程,我想知道重构bellow函数的最佳方法是什么,以了解最佳实践 我正在重构我制作的一个面向对象的粒子系统,这里首先是我作为一个类编写的代码: submitToFields(fields) { let totalAccelerationX = 0; let totalAccelerationY = 0; fields.forEach(field => { const vectorX = field.position.x - this.p

我正在学习函数式编程,我想知道重构bellow函数的最佳方法是什么,以了解最佳实践

我正在重构我制作的一个面向对象的粒子系统,这里首先是我作为一个类编写的代码:

submitToFields(fields) {
    let totalAccelerationX = 0;
    let totalAccelerationY = 0;
    fields.forEach(field => {
      const vectorX = field.position.x - this.position.x;
      const vectorY = field.position.y - this.position.y;
      const force = Particle.calculateForce(field, vectorX, vectorY);
      totalAccelerationX += vectorX * force;
      totalAccelerationY += vectorY * force;
    });
    this.acceleration = new Vector(totalAccelerationX, totalAccelerationY);
  }
  • 该方法属于具有位置、速度、加速度等特性的粒子对象
  • fiels是一个字段对象数组,其结构类似于{position:{x:0,y:0},mass:140,…}
  • 向量是类似于{x:0,y:0}的对象

  • 该方法计算粒子系统上的场(重力质量)对粒子的扰动加速度

好了,这就是我到目前为止所得到的,但我想更多地钻研一下函数式编程,试着变得更无点,函数式编程可以提供给我的其他好处


const add = a => b => a + b;
const propX = R.prop('x');
const propY = R.prop('y');
const addVectors = (vectorA, vectorB) =>
  vector(
    add(propX(vectorA))(propX(vectorB)),
    add(propY(vectorA))(propY(vectorB)),
  );
const vector = (a, b) => ({ x: a, y: b });
const square = x => x ** 2;
const difference = a => b => a - b;
const position = item => R.prop('position', item) || 0;
const posX = item => R.prop('x', position(item)) || 0;
const posY = item => R.prop('y', position(item)) || 0;
const propMass = item => R.prop('mass', item) || 0;

const calculateForce = (mass, vectr) => {
  return mass / (square(vectr.x) + square(vectr.y) + mass) ** 1.5;
};


const disturbanceAcceleration = (part, fields) => {
  const partX = posX(part);
  const partY = posY(part);
  return fields.reduce((acc, curr) => {
    const fieldX = difference(posX(curr))(partX);
    const fieldY = difference(posY(curr))(partY);
    const force = calculateForce(propMass(curr), vector(fieldX, fieldY));
    const newVector = vector(fieldX * force, fieldY * force);
    return addVectors(acc, newVector);
  }, vector(0, 0));
};
下面是一个测试:

鉴于:

const part = {position:{x:0,y:0}};

const fields = [
 {position:{x:100,y:100}, mass:140},
 {position:{x:200,y:100}, mass:140} , 
 {position:{x:150,y:300}, mass:140}];
执行:

disturbanceAcceleration(part,fields)
输出:

{x: 0.007947635786319896, y: 0.007256173830876778}

Vector.js

从基本的
向量
模块开始,我们使用对象来保存
x
y
属性,但我们确保向量操作
添加
差异
缩放
不会改变对象;相反,总是返回一个新的向量-

const Vector = (x = 0, y = 0) =>
  ({ x, y })

Vector.add = (a = Vector (), b = Vector ()) =>
  Vector
    ( a.x + b.x
    , a.y + b.y
    )

Vector.difference = (a = Vector (), b = Vector ()) =>
  Vector
    ( a.x - b.x
    , a.y - b.y
    )

Vector.scale = ({ x, y } = Vector (), k = 1) =>
  Vector
    ( x * k
    , y * k
    )

// ...

export default Vector
Field.js

然后我们构建一个
字段
模块,它依赖于
向量
。同样,我们确保不使用可变操作-

const Vector =
  require ('./Vector')

const Field = (mass = 0, position = Vector ()) =>
  ({ mass, position })

Field.calculateForce = ({ mass, position:{ x, y } } = Field ()) =>
  mass / (x ** 2 + y ** 2 + mass) ** 1.5

Field.disturbanceAcceleration = (origin, fields = []) =>
  fields.reduce
    ( (acc, { mass, position }) =>
      { const v =
          Vector.difference (position, origin)

        const force =
          Field.calculateForce (Field (mass, v))

        return Vector.add
          ( acc
          , Vector.scale (v, force)
          )
      }
    , Vector ()
    )

// ...

export default Field
现在是我们的解决方案-

import Vector from './Vector'
import Field from './Field'

const fields =
  [ Field (140, Vector (100, 100))
  , Field (140, Vector (200, 100))
  , Field (140, Vector (150, 300))
  ]

Field.disturbanceAcceleration (Vector (), fields)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
import { Vector, Field /*, ... */ } from ('myparticle')

// ...
默认参数的使用为我们提供了极其方便的数据类型使用。这些都是我们不认为理所当然的事情-

Vector ()         // { x: 0, y: 0 }
Vector () .x      // 0
Vector () .y      // 0
Vector (1, 2) .x  // 1
Vector (1, 2) .y  // 2

Field ()                                 // { mass: 0, position: { x: 0, y: 0 } }
Field () .mass                           // 0
Field () .position                       // { x: 0, y: 0 }
Field (100) .mass                        // 100
Field (100) .position                    // { x: 0, y: 0 }
Field (100, Vector (1, 2)) .mass         // 100
Field (100, Vector (1, 2)) .position .x  // 1
Field (100, Vector (1, 2)) .position .y  // 2
myparticle.js

你可能会有很多子模块,这没关系。您可能希望将子模块组合成一个更大的模块,也许我们称之为myparticle-

然后当我们在解决方案中使用它时-

import Vector from './Vector'
import Field from './Field'

const fields =
  [ Field (140, Vector (100, 100))
  , Field (140, Vector (200, 100))
  , Field (140, Vector (150, 300))
  ]

Field.disturbanceAcceleration (Vector (), fields)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
import { Vector, Field /*, ... */ } from ('myparticle')

// ...

代码演示

展开下面的代码段,在您自己的浏览器中验证结果-

//向量-------------------------------------------
常数向量=(x=0,y=0)=>
({x,y})
Vector.add=(a=Vector(),b=Vector())=>
矢量
(a.x+b.x
,a.y+b.y
)
Vector.difference=(a=Vector(),b=Vector())=>
矢量
(a.x-b.x)
,a.y-b.y
)
Vector.scale=({x,y}=Vector(),k=1)=>
矢量
(x*k)
,y*k
)
//场-------------------------------------------
常量字段=(质量=0,位置=向量())=>
({质量,位置})
Field.calculateForce=({mass,position:{x,y}}}=Field())=>
质量/(x**2+y**2+质量)**1.5
Field.disturbanceAcceleration=(原点,字段=[])=>
字段。减少
((acc,{质量,位置})=>
{const v=
矢量差(位置、原点)
恒力=
Field.calculateForce(场(质量,v))
返回向量.add
(根据
,向量。比例(v,力)
)
}
,向量()
)
//演示-------------------------------------------
常量字段=
[字段(140,向量(100100))
,字段(140,向量(200100))
,字段(140,向量(150300))
]
常数结果=
Field.disturbanceAcceleration(向量(),字段)
console.log(结果)

//{x:0.007947635786319896,y:0.007256173830876778}
Vector.js

从基本的
向量
模块开始,我们使用对象来保存
x
y
属性,但我们确保向量操作
添加
差异
缩放
不会改变对象;相反,总是返回一个新的向量-

const Vector = (x = 0, y = 0) =>
  ({ x, y })

Vector.add = (a = Vector (), b = Vector ()) =>
  Vector
    ( a.x + b.x
    , a.y + b.y
    )

Vector.difference = (a = Vector (), b = Vector ()) =>
  Vector
    ( a.x - b.x
    , a.y - b.y
    )

Vector.scale = ({ x, y } = Vector (), k = 1) =>
  Vector
    ( x * k
    , y * k
    )

// ...

export default Vector
Field.js

然后我们构建一个
字段
模块,它依赖于
向量
。同样,我们确保不使用可变操作-

const Vector =
  require ('./Vector')

const Field = (mass = 0, position = Vector ()) =>
  ({ mass, position })

Field.calculateForce = ({ mass, position:{ x, y } } = Field ()) =>
  mass / (x ** 2 + y ** 2 + mass) ** 1.5

Field.disturbanceAcceleration = (origin, fields = []) =>
  fields.reduce
    ( (acc, { mass, position }) =>
      { const v =
          Vector.difference (position, origin)

        const force =
          Field.calculateForce (Field (mass, v))

        return Vector.add
          ( acc
          , Vector.scale (v, force)
          )
      }
    , Vector ()
    )

// ...

export default Field
现在是我们的解决方案-

import Vector from './Vector'
import Field from './Field'

const fields =
  [ Field (140, Vector (100, 100))
  , Field (140, Vector (200, 100))
  , Field (140, Vector (150, 300))
  ]

Field.disturbanceAcceleration (Vector (), fields)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
import { Vector, Field /*, ... */ } from ('myparticle')

// ...
默认参数的使用为我们提供了极其方便的数据类型使用。这些都是我们不认为理所当然的事情-

Vector ()         // { x: 0, y: 0 }
Vector () .x      // 0
Vector () .y      // 0
Vector (1, 2) .x  // 1
Vector (1, 2) .y  // 2

Field ()                                 // { mass: 0, position: { x: 0, y: 0 } }
Field () .mass                           // 0
Field () .position                       // { x: 0, y: 0 }
Field (100) .mass                        // 100
Field (100) .position                    // { x: 0, y: 0 }
Field (100, Vector (1, 2)) .mass         // 100
Field (100, Vector (1, 2)) .position .x  // 1
Field (100, Vector (1, 2)) .position .y  // 2
myparticle.js

你可能会有很多子模块,这没关系。您可能希望将子模块组合成一个更大的模块,也许我们称之为myparticle-

然后当我们在解决方案中使用它时-

import Vector from './Vector'
import Field from './Field'

const fields =
  [ Field (140, Vector (100, 100))
  , Field (140, Vector (200, 100))
  , Field (140, Vector (150, 300))
  ]

Field.disturbanceAcceleration (Vector (), fields)
// { x: 0.007947635786319896, y: 0.007256173830876778 }
import { Vector, Field /*, ... */ } from ('myparticle')

// ...

代码演示

展开下面的代码段,在您自己的浏览器中验证结果-

//向量-------------------------------------------
常数向量=(x=0,y=0)=>
({x,y})
Vector.add=(a=Vector(),b=Vector())=>
矢量
(a.x+b.x
,a.y+b.y
)
Vector.difference=(a=Vector(),b=Vector())=>
矢量
(a.x-b.x)
,a.y-b.y
)
Vector.scale=({x,y}=Vector(),k=1)=>
矢量
(x*k)
,y*k
)
//场-------------------------------------------
常量字段=(质量=0,位置=向量())=>
({质量,位置})
Field.calculateForce=({mass,position:{x,y}}}=Field())=>
质量/(x**2+y**2+质量)**1.5
Field.disturbanceAcceleration=(原点,字段=[])=>
字段。减少
((acc,{质量,位置})=>
{const v=
矢量差(位置、原点)
恒力=
Field.calculateForce(场(质量,v))
返回向量.add
(根据
,向量。比例(v,力)
)
}
,向量()
)
//演示-------------------------------------------
常量字段=
[字段(140,向量(100100))
,字段(140,向量(200100))
,字段(140,向量(150300))
]
常数结果=
Field.disturbanceAcceleration(向量(),字段)
console.log(结果)

//{x:0.007947635786319896,y:0.007256173830876778}
当然,您可以在
disturbanceAcceleration
中创建复杂构图以避免中间值。但这不会给你太多。在某种复杂程度上,中间结果有助于跟踪