您的问题是关于如何在 Javascript 中使用镜头?如果是这样,我也许可以提供帮助。你检查过Ramda.js 库吗?这是编写函数式 JS 的绝佳方式。让我们先看看你的敌人模型:
/* -- data model -- */
let enemyModel = {
name: "badguy1",
stats: {
health: 10,
strength: 42
},
pos: {
x: 100,
y: 101
}
};
镜头:为了构建镜头,您需要针对特定对象的 getter 方法和 setter 方法 - 在您的情况下是“敌人”。这是您可以手动构建它们的方法。
方法 1:创建自己的 getter 和 setter
const getHealth = path(['stats', 'health']);
const setHealth = assocPath(['stats', 'health']);
const healthLens = lens(getHealth, setHealth);
方法 2: Ramda 的权宜之计的 Objects 镜头
const healthLens = lensPath(['stats', 'health']);
创建镜头后,就可以使用它了。Ramda 提供 3 种使用镜头的功能:view(..)
、set(..)
和over(..)
。
view(healthLens)(enemyModel); // 10
set(healthLens, 15)(enemyModel); // changes health from 10 to 15
over(healthLens, fireDamage)(enemyModel); // reduces enemyModel's health property by 10
由于您将该fireDamage(..)
功能应用于敌人的健康状况,因此您需要使用over(..)
. 此外,由于您的位置坐标嵌套在enemyModel 中,因此您还需要使用镜头来访问它们。让我们创建一个并重构isInRange(..)
它。
作为参考,这里是原点 fn:
// NOTE: not sure if this works as you intended it to...
function isInRange(radius, point) {
return point.x^2 + point.y^2 >= radius^2; // maybe try Math.pow(..)
}
这是一种功能方法:
/* -- helper functions -- */
const square = x => x * x;
const gteRadSquared = radius => flip(gte)(square(radius));
let sumPointSquared = point => converge(
add,
[compose(square, prop('x')),
compose(square, prop('y'))]
)(point);
sumPointSquared = curry(sumPointSquared); // allows for "partial application" of fn arguments
/* -- refactored fn -- */
let isInRange = (radius, point) => compose(
gteRadSquared(radius),
sumPointSquared
)(point);
isInRange = curry(isInRange);
这是处理一组敌人模型时的样子:
/* -- lenses -- */
const xLens = lensPath(['pos', 'x']);
const yLens = lensPath(['pos', 'y']);
const ptLens = lens(prop('pos'), assoc('pos'));
// since idk where 'radius' is coming from I'll hard-code it
let radius = 12;
const filterInRange = rad => filter(
over(ptLens, isInRange(rad)) // using 'ptLens' bc isInRange(..) takes 'radius' and a 'point'
);
const mapFireDamage = map(
over(healthLens, fireDamage) // using 'healthLens' bc fireDamage(..) takes 'health'
);
let newEnemies = compose(
mapFireDamage,
filterInRange(radius)
)(enemies);
我希望这有助于说明镜头的用途。虽然有很多辅助函数,但我认为最后一段代码是超级语义的!
最后,我只是用 Ramda 的这些函数来扩展我的范围,以使这个示例更具可读性。我正在使用 ES6 解构来实现这一点。就是这样:
const {
add,
assocPath,
compose,
converge,
curry,
filter,
flip,
gte,
lens,
lensPath,
map,
over,
set,
path,
prop,
view
} = R;
// code goes below...
在 jsBin 中尝试一下!他们提供 Ramda 支持。