简体   繁体   English

如何避免JavaScript中函数输出的多个变量重新声明

[英]How to avoid multiple variable re-declarations on function outputs in JavaScript

Consider the following example 请考虑以下示例

function doSomethingToAVariable(variable){
    return variable + 1
}

function doSomethingToAVariableASecondTime(variable){
    return variable + 2
}

function doSomethingToAVariableLastly(variable){
    return variable + 3
}

var myVariable = 0;
myVariable = doSomethingToAVariable(myVariable);
myVariable = doSomethingToAVariableASecondTime(myVariable);
myVariable = doSomethingToAVariableLastly(myVariable);

console.log(myVariable); // 6

How do I avoid the nasty myVariable re-declarations? 我如何避免令人讨厌的myVariable重新声明? Could wrapping each of the functions into a Promise chain be a solution? 可以将每个函数包装到Promise链中作为解决方案吗?

Function composition to the rescue. 功能组成对救援。
Take a look at libraries for functional programming, like Ramda, or lodash-fp. 看一下函数式编程库,比如Ramda或lodash-fp。

here a plain JS snippet to compose functions: 这里有一个简单的JS片段来组成函数:

//the compose-method you find in your regular FP-libs
var compose = (...funcs) => (value) => funcs.reduceRight((v,fn)=>fn(v), value);
//or a function wich takes the functions in opposite order, 
//wich might be more common to you
var pipe = (...funcs) => (value) => funcs.reduce((v,fn)=>fn(v), value);

compose is a direct mapping of the composition you try to build compose是您尝试构建的组合的直接映射

var composition = (value) => a(b(c(value)));
var composition = compose(a, b, c);
//it calls the functions from right to left

pipe is more oriented on your known imperative style to process a value step by step 管道更倾向于您已知的命令式样式,以逐步处理值

var composition = function(value){
    value = c(value);
    value = b(value);
    value = a(value);
    return value;
}
//pipe the value through c, then through b, then through a
var fn = pipe(c, b, a);
//wich in the end does exactly the same as the code built by compose

so back to your code: 所以回到你的代码:

var composition = pipe(
    doSomethingToAVariable,
    doSomethingToAVariableASecondTime,
    doSomethingToAVariableLastly
);
//or
var composition = compose(
    doSomethingToAVariableLastly,
    doSomethingToAVariableASecondTime,
    doSomethingToAVariable
);

//and run it
var myVariable = composition(0);

If you want to chain then you need to return the object instead which will contain the final value 如果你想链接,那么你需要返回包含最终值的对象

function variableOperations( initialValue )
{
  this.value = initialValue;
  this.someOp1 = function(){ this.value += 1; return this; } 
  this.someOp2 = function(){ this.value += 2; return this; } 
}

var a = new variableOperations(1); //new object
a.someOp1().someOp2();

alert(a.value); //alerts 4

In this case you're looking for what's called "function composition": 在这种情况下,您正在寻找所谓的“功能组合”:

var myVariable = doSomethingToAVariableLastly(doSomethingToAVariableASecondTime(doSomethingToAVariable(0)));

but this is clearly not readable with such long function names. 但是这些长函数名称显然无法读取。

Promises are typically only useful for asynchronous operations, and whilst they'd work in this scenario, the result would be inefficient and would introduce async dependencies where none are needed: Promise通常仅对异步操作有用,虽然它们在这种情况下工作,但结果效率低下并且会引入异步依赖项,而不需要它们:

var promise = doSomethingToAVariable(0);
        .then(doSomethingToAVariableASecondTime);
        .then(doSomethingToAVariableLastly);

since you can only access the final result from the end of the chain of .then callbacks: 因为你只能从.then回调链的末尾访问最终结果:

promise.then(function(myVariable) {
    // myVariable is now 6, and in scope
}

// but it's not in scope or usable here

Why not take a look at a functional approach with Ramda? 为什么不看看Ramda的功能方法呢?

R.compose(
  doSomethingToAVariableLastly,
  doSomethingToAVariableASecondTime,  
  doSomethingToAVariable
)(myVariable)

In my humble opinion, your code is perfectly fine. 以我的拙见,你的代码非常好。

But, you can do something like this if you really want to "chain" the calls. 但是,如果你真的想要“链接”这些电话,你可以做这样的事情。 It is a bit less efficient though. 但效率稍低。

 function chainCalls(initVal, funcs){ return funcs.reduce(function(val, f){ return f(val); }, initVal || 0); } function doSomethingToAVariable(variable){ return variable + 1 } function doSomethingToAVariableASecondTime(variable){ return variable + 2 } function doSomethingToAVariableLastly(variable){ return variable + 3 } var myVariable = chainCalls(0, [doSomethingToAVariable, doSomethingToAVariableASecondTime, doSomethingToAVariableLastly]); document.write(myVariable); 

Another alternative is to create a reusable function chain like this. 另一种方法是创建一个像这样的可重用功能链。

 function functionChain(){ var funcs = arguments; return function(value){ return Array.prototype.reduce.call(funcs, function(value, f){ return f(value); }, value); }; } function doSomethingToAVariable(variable){ return variable + 1 } function doSomethingToAVariableASecondTime(variable){ return variable + 2 } function doSomethingToAVariableLastly(variable){ return variable + 3 } var myVariable = functionChain( doSomethingToAVariable, doSomethingToAVariableASecondTime, doSomethingToAVariableLastly )(0); document.write(myVariable); 

If it's as simple as adding some things together how about a recursive method. 如果只是将一些东西放在一起就像递归方法一样简单。 A bit like using promises but without actually using them. 有点像使用promises但没有实际使用它们。

function add(x) {
  return {
    done: () => x,
    add: (y) => add(x + y)
  }
}

let myVariable = 0;
add(myVariable).add(1).add(2).add(3).done(); // 6

DEMO DEMO

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM