Câu hỏi của bạn là về cách sử dụng ống kính trong Javascript? Nếu có, tôi có thể giúp. Bạn đã xem qua số Ramda.js library chưa? Đó là một cách tuyệt vời để viết JS chức năng. Hãy bắt đầu bằng cách nhìn vào mô hình kẻ thù của bạn:
/* -- data model -- */
let enemyModel = {
name: "badguy1",
stats: {
health: 10,
strength: 42
},
pos: {
x: 100,
y: 101
}
};
Lens: Để xây dựng một ống kính bạn cần một phương thức getter và setter một phương pháp cho đối tượng cụ thể của bạn - trong trường hợp của bạn là "kẻ thù". Đây là cách bạn có thể xây dựng chúng bằng tay.
Phương pháp 1: Tạo getter và setter
const getHealth = path(['stats', 'health']);
const setHealth = assocPath(['stats', 'health']);
const healthLens = lens(getHealth, setHealth);
Phương pháp 2 của riêng bạn: thích hợp thuận tiện ống kính Ramda cho đối tượng
const healthLens = lensPath(['stats', 'health']);
Khi bạn đã tạo ống kính, đó là thời gian để sử dụng nó. Ramda cung cấp 3 chức năng để sử dụng ống kính: view(..)
, set(..)
và 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
Vì bạn đang áp dụng fireDamage(..)
chức năng đối với sức khỏe của kẻ thù, bạn sẽ muốn sử dụng over(..)
. Ngoài ra, vì tọa độ vị trí của bạn được lồng trong enemyModel, bạn sẽ muốn sử dụng một ống kính để truy cập chúng. Hãy tạo một và refactor isInRange(..)
trong khi chúng ta đang ở đó.
Là một tài liệu tham khảo, đây là fn xứ:
// 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(..)
}
Dưới đây là một cách tiếp cận chức năng:
/* -- 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);
Đây là những gì mà sẽ trông như thế khi giao dịch với một bộ sưu tập của enemyModels:
/* -- 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);
Tôi hy vọng điều này sẽ giúp minh họa các ống kính hữu ích có thể như thế nào.Trong khi có nhiều chức năng trợ giúp, tôi nghĩ đoạn cuối của mã là siêu ngữ nghĩa!
Cuối cùng, tôi chỉ tràn ngập phạm vi của mình với các chức năng này từ Ramda để làm cho ví dụ này dễ đọc hơn. Tôi đang sử dụng ES6 deconstruction để thực hiện điều này. Dưới đây là cách thực hiện:
const {
add,
assocPath,
compose,
converge,
curry,
filter,
flip,
gte,
lens,
lensPath,
map,
over,
set,
path,
prop,
view
} = R;
// code goes below...
Hãy dùng thử trong jsBin! Họ cung cấp hỗ trợ Ramda.
@DustinGetz Vì vậy, bạn có nghĩa là bạn muốn dịch sang Javascript? –
@DustinGetz Bạn đang cố gắng làm điều đó một cách chính xác theo phong cách của thư viện 'thấu kính' (tức là chức năng của các chức năng?) Hay bạn chỉ đang tạo ra các getters và setters hạng nhất? –
Giá trị của tôi được lồng sâu hơn, nhưng không phải là biểu đồ. Nói sâu 4. Tôi nghĩ rằng chức năng của các chức năng là cần thiết. –