Javascript 我应该如何在小函数之间传递数据——通过闭包还是通过对象的属性?
我有一个复杂的业务操作,例如删除用户帐户。它包含多个连接的步骤,并且必须跟踪步骤之间的某些状态。编写此Javascript 我应该如何在小函数之间传递数据——通过闭包还是通过对象的属性?,javascript,node.js,oop,design-patterns,functional-programming,Javascript,Node.js,Oop,Design Patterns,Functional Programming,我有一个复杂的业务操作,例如删除用户帐户。它包含多个连接的步骤,并且必须跟踪步骤之间的某些状态。编写此操作的更好方法是什么 我看到了很多类似下面的功能性方法 function someAction(someParam, anotherParam, callback) { async.waterfall([ step1, step2, step3, step4 ],ca
操作
的更好方法是什么
我看到了很多类似下面的功能性方法
function someAction(someParam, anotherParam, callback) {
async.waterfall([
step1,
step2,
step3,
step4
],callback
);
function step1(p,cb){/**use someParam and anotherParam here via closure*/}
function step2(p,cb){/**...*/}
function step3(p,cb){/**...*/}
function step4(p,cb){/**...*/}
};
someAction('value', 1241, (err)=>{/**...*/});
我不喜欢这种方法的地方在于,所有内容都是在单个函数的范围内定义的(这里是someAction
)
我找到了一种更具可读性的面向对象的方法。状态和stepX
函数不是真正私有的-有时它便于测试
function SomeAction(someParam, anotherParam){
//private state
this._someParam = someParam;
this._anotherParam = anotherParam;
};
SomeAction.prototype._step1 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step2 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step3 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step4 = function(p, cb){
//use this._someParam and this._anotherParam
};
//public api
SomeAction.prototype.execute = function(callback) {
async.waterfall([
this._step1,
this._step2,
this._step3,
this._step4
],callback
)
};
new SomeAction('value', 1241).execute((err)=>{/**...*/})
它们之间有什么性能差异吗?Node.js中推荐的方法是什么?每次我在函数方法中调用someAction
,所有stepX
函数都必须从头定义,这是真的吗 您可以创建步骤函数的咖喱版本,并将它们粘贴到瀑布中
function step1(arg1, arg2, cb){
// Fuction body...
}
// other steps would be defined here...
function step4(arg1, cb){
// fuction body...
}
curried_step1 = step1.bind(undefined, 'junk', 'garb');
// Other steps curried here...
curried_step4 = step4.bind(undefined, 'something');
async.waterfall([
curried_step1,
curried_step2,
curried_step3,
curried_step4
],callback
);
另一种方法是将数据和状态包装到一个对象中(代替真正的monad),并使用该对象传递您需要的内容。您可以创建step函数的通用版本,并将它们粘贴到瀑布中
function step1(arg1, arg2, cb){
// Fuction body...
}
// other steps would be defined here...
function step4(arg1, cb){
// fuction body...
}
curried_step1 = step1.bind(undefined, 'junk', 'garb');
// Other steps curried here...
curried_step4 = step4.bind(undefined, 'something');
async.waterfall([
curried_step1,
curried_step2,
curried_step3,
curried_step4
],callback
);
另一种方法是将数据和状态包装成一个对象(代替真正的monad),并使用该对象传递您需要的信息。您可以使用Promissions,其样式如下所示:
var Promise = require('promise');
//** Execute Program **//
main();
/**
* Do your async logic flow here
*/
function main() {
step1()
.then(step2) //Wait for step1 to finish, and pass the response directly to step2
.then(function(res) {
// Do something with res (the return from step2)
// Async execute step3 & step4
var promises = [
step3(),
step4()
];
// Wait for step3 & step4 to finish:
Promise.all([promises[0], promises[1]]).then(function(values) {
console.log(values[0]); //Value from step3
console.log(values[1]); //Value from step4
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps3 or 4
});
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps1 or 2
});
}
function step1() {
return new Promise(resolve, reject) {
//some async task here
resolve('step1');
//reject('step1'); //Or trigger a .catch (i.e. this function failed)
});
}
function step2() {
return new Promise(resolve, reject) {
//some async task here
resolve('step2');
//reject('step2'); //Or trigger a .catch (i.e. this function failed)
});
}
function step3() {
return new Promise(resolve, reject) {
//some async task here
resolve('step3');
//reject('step3'); //Or trigger a .catch (i.e. this function failed)
});
}
function step4() {
return new Promise(resolve, reject) {
//some async task here
resolve('step4');
//reject('step4'); //Or trigger a .catch (i.e. this function failed)
});
}
您可以使用Promissions(承诺)来创建如下样式:
var Promise = require('promise');
//** Execute Program **//
main();
/**
* Do your async logic flow here
*/
function main() {
step1()
.then(step2) //Wait for step1 to finish, and pass the response directly to step2
.then(function(res) {
// Do something with res (the return from step2)
// Async execute step3 & step4
var promises = [
step3(),
step4()
];
// Wait for step3 & step4 to finish:
Promise.all([promises[0], promises[1]]).then(function(values) {
console.log(values[0]); //Value from step3
console.log(values[1]); //Value from step4
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps3 or 4
});
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps1 or 2
});
}
function step1() {
return new Promise(resolve, reject) {
//some async task here
resolve('step1');
//reject('step1'); //Or trigger a .catch (i.e. this function failed)
});
}
function step2() {
return new Promise(resolve, reject) {
//some async task here
resolve('step2');
//reject('step2'); //Or trigger a .catch (i.e. this function failed)
});
}
function step3() {
return new Promise(resolve, reject) {
//some async task here
resolve('step3');
//reject('step3'); //Or trigger a .catch (i.e. this function failed)
});
}
function step4() {
return new Promise(resolve, reject) {
//some async task here
resolve('step4');
//reject('step4'); //Or trigger a .catch (i.e. this function failed)
});
}
这不完全是一个答案,但回答了您的间接问题:
问题是我应该如何在这些小函数之间传递数据——通过闭包或通过对象的属性
还有第三条路。如果您熟悉OO设计,那么您可能熟悉命令模式的概念。也就是说,您需要动态构造一个函数,但这是不可能的,因此您可以使用一种方法创建一个对象,然后根据对象的属性进行自定义
在函数式编程中,此设计模式相当于函数工厂模式。基本上,您编写一个函数来生成另一个函数
因此,您希望将someParam
和anotherParam
传递给异步函数,但希望能够在someAction
函数之外编写该函数。以下是您如何做到这一点:
function someAction (someParam, anotherParam, callback) {
async.waterfall([
make_step1(someParam,anotherParam),
make_step2(someParam,anotherParam)
/* ... */
],callback
);
}
function make_step1 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
function make_step2 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
// ...
这消除了您对函数代码提出的主要异议:您不再需要在someAction()
中定义所有步骤函数,这使得它看起来更类似于OO代码
每次调用someAction()
时,仍然会创建所有步骤函数的新实例(现在仅从make函数返回)。但是解释器不必再次编译函数。相反,只创建一个新的闭包(将闭包视为与程序堆栈解除链接的冻结堆栈帧)。这不是一个完全正确的答案,而是回答了您的间接问题:
问题是我应该如何在这些小函数之间传递数据——通过闭包或通过对象的属性
还有第三条路。如果您熟悉OO设计,那么您可能熟悉命令模式的概念。也就是说,您需要动态构造一个函数,但这是不可能的,因此您可以使用一种方法创建一个对象,然后根据对象的属性进行自定义
在函数式编程中,此设计模式相当于函数工厂模式。基本上,您编写一个函数来生成另一个函数
因此,您希望将someParam
和anotherParam
传递给异步函数,但希望能够在someAction
函数之外编写该函数。以下是您如何做到这一点:
function someAction (someParam, anotherParam, callback) {
async.waterfall([
make_step1(someParam,anotherParam),
make_step2(someParam,anotherParam)
/* ... */
],callback
);
}
function make_step1 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
function make_step2 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
// ...
这消除了您对函数代码提出的主要异议:您不再需要在someAction()
中定义所有步骤函数,这使得它看起来更类似于OO代码
每次调用someAction()
时,仍然会创建所有步骤函数的新实例(现在仅从make函数返回)。但是解释器不必再次编译函数。相反,只创建一个新的闭包(将闭包视为与程序堆栈断开链接的冻结堆栈框架)。建议的方法是更易于阅读且符合您的要求的方法(可测试性)。性能差异可以忽略不计。最好的解决方案是使用承诺。它们既是对象又是功能性的。功能性并不意味着将整个逻辑放在一个功能中。这意味着设计每个具有单一用途的小功能,然后组合这些功能以形成更复杂的功能。使用功能范例,您不会获得封装,而是代码重用,并且由于没有副作用而提高了可维护性。@LUH3417感谢您的解释!我介绍的两种方法都由许多小函数组成(我喜欢这种风格——由Bob叔叔推荐)。问题是我应该如何在这些小函数之间传递数据——通过闭包或通过对象的属性。bob叔叔建议类是一个用来存放数据和操作数据的方法的袋子。现在我明白了。我很困惑,因为您在包装器中定义了stepX
函数(当然是为了关闭它们),因此无法再重用它们。您可以将不断变化的状态作为附加参数传递,并使用状态单子将其抽象掉。如果共享状态没有改变,读卡器monad是另一种选择。另一个问题是您是否使用不可变数据。归根结底,我认为这个问题太宽泛了。推荐的方法是