我有一个不纯函数的例子。请注意,变量a
在函数范围之外。为了解决它,可以克隆函数内的对象并返回一个副本,但这是正确的方法吗?
有哪些选项可以使函数transformObject
变为纯粹的?
var a = {
a : 1,
b : 2
};
function transformObject(obj) {
var ref = obj;
_.each(ref, function(val, index){
ref[index] = val*2;
});
return ref;
}
s=JSON.stringify
$('#code').text(s(transformObject(a)))
$('#code2').text(s(a))
答案 0 :(得分:2)
纯函数遵循三个主要原则:
该功能应该只做一件事。
该功能不会改变其范围之外的状态。
该功能为相同的输入输出相同的值。
下一步是探索这些原则在功能设计中的影响。
你的功能已经符合第一个原则,虽然名称应该改进;)
它也符合第二个原则,因为它不会修改其范围之外的任何其他东西(它的输入参数)。
不幸的是,它不符合第三个原则:
var a = {
a : 1,
b : 2
};
var b = transformObject(a);//b = {2, 4}
var c = transformObject(a);//c = {4, 8}
相同的输入(a
)但输出不同。这违反了第三条原则。
参考透明函数需要不可变数据。
Nina Scholz已经发布了一个很好的答案:
function transformObject(a) {
var b = {};
Object.keys(a).forEach(function (k) { b[k] = a[k] * 2; });
return b;
}
但它只能起作用,因为输入对象不包含嵌套在其中的任何其他对象。
现在有一些好的库可以为你提供不可变的结构(比如ImmutableJS)。
使用ImmutableJS的简单示例:
describe('a List', () => {
function addMovie(currentState, movie) {
return currentState.push(movie);
}
it('is immutable', () => {
let state = List.of('Avengers', 'Antman');
let nextState = addMovie(state, 'Superman');
expect(nextState).to.equal(List.of(
'Avengers',
'Antman',
'Superman'
));
expect(state).to.equal(List.of(
'Avengers',
'Antman'
));
});
it('can be updated and returns another immutable List', () => {
let state = List.of(1,2,3,4);
let nextState = state.map(value => value*2);
expect(nextState).to.equal(List.of(2,4,6,8));
expect(state).to.equal(List.of(1,2,3,4));
});
});
您可以阅读有关不可变API here
的更多信息答案 1 :(得分:1)
更改对象:
function transformObject(a) {
Object.keys(a).forEach(function (k) { a[k] *= 2; });
}
var a = {
a : 1,
b : 2
};
transformObject(a);
document.write('<pre>' + JSON.stringify(a, 0, 4) + '</pre>');
保留对象并返回一个带有结果的新对象:
function transformObject(a) {
var b = {};
Object.keys(a).forEach(function (k) { b[k] = a[k] * 2; });
return b;
}
var a = {
a : 1,
b : 2
},
b = transformObject(a);
document.write('<pre>' + JSON.stringify(a, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(b, 0, 4) + '</pre>');