2016-04-16 32 views
4

我正在读一本书,包含了下面的例子:的JavaScript编写功能

var composition1 = function(f, g) { 
    return function(x) { 
    return f(g(x)); 
    } 
}; 

然后作者写道:” ......天真实施组成,因为它并不需要执行上下文考虑.. ”

所以优选功能是一个:

var composition2 = function(f, g) { 
    return function() { 
    return f.call(this, g.apply(this, arguments)); 
    } 
}; 

随后的整个例子:

var composition2 = function composition2(f, g) { 
    return function() { 
     return f.call(this, g.apply(this, arguments)); 
    } 
}; 

var addFour = function addFour(x) { 
    return x + 4; 
}; 

var timesSeven = function timesSeven(x) { 
    return x * 7; 
}; 

var addFourtimesSeven2 = composition2(timesSeven, addFour); 
var result2 = addFourtimesSeven2(2); 
console.log(result2); 

难道有人请向我解释为什么组合函数是首选的(可能是一个例子)?

编辑:

在此期间我曾尝试使用方法作为参数的建议,但没有奏效。其结果是楠:如果你

var composition1 = function composition1(f, g) { 
    return function(x) { 
     return f(g(x)); 
    }; 
}; 

var composition2 = function composition2(f, g) { 
    return function() { 
     return f.call(this, g.apply(this, arguments)); 
    } 
}; 

var addFour = { 
    myMethod: function addFour(x) { 
     return x + this.number; 
    }, 
    number: 4 
}; 

var timesSeven = { 
    myMethod: function timesSeven(x) { 
     return x * this.number; 
    }, 
    number: 7 
}; 

var addFourtimesSeven1 = composition1(timesSeven.myMethod, addFour.myMethod); 
var result1 = addFourtimesSeven1(2); 
console.log(result1); 

var addFourtimesSeven2 = composition2(timesSeven.myMethod, addFour.myMethod); 
var result2 = addFourtimesSeven2(2); 
console.log(result2); 
+0

这实际上与咖喱无关。 – Bergi

+0

你说得对,我改了措辞。 – JShinigami

+0

它被称为功能组成!为什么你为了天堂而撰写方法?函数组合意味着以无咖啡因的方式将咖啡形式的纯函数组合在一起,而无副作用:'const compose = f => g => x => f(g(x))'。除了只有一个参数的curried函数(就像我们的'compose'函数)。函数组合仅适用于curried函数,因为函数只返回单个值。 – ftor

回答

1

这只是回答了什么composition2实际上做:当你想保持this作为功能本身背景下

composition2使用。下面的例子显示的结果是60使用data.adata.b

'use strict'; 

var multiply = function(value) { 
    return value * this.a; 
} 
var add = function(value) { 
    return value + this.b; 
} 

var data = { 
    a: 10, 
    b: 4, 
    func: composition2(multiply, add) 
}; 

var result = data.func(2); 
// uses 'data' as 'this' inside the 'add' and 'multiply' functions 
// (2 + 4) * 10 = 60 

不过不失,但仍打破了下面的例子(不幸):

'use strict'; 

function Foo() { 
    this.a = 10; 
    this.b = 4; 
} 
Foo.prototype.multiply = function(value) { 
    return value * this.a; 
}; 
Foo.prototype.add = function(value) { 
    return value + this.b; 
}; 


var foo = new Foo(); 

var func = composition2(foo.multiply, foo.add); 
var result = func(2); // Uncaught TypeError: Cannot read property 'b' of undefined 

由于composition2this)上下文是未定义的(并且不以任何其他方式调用,例如.apply,.callobj.func()),那么最终在函数中也将定义this未定义。

在另一方面,我们可以通过下面的代码给它的另一个方面:

'use strict'; 
var foo = new Foo(); 

var data = { 
    a: 20, 
    b: 8, 
    func: composition2(foo.multiply, foo.add) 
} 

var result = data.func(2); 
// uses 'data' as 'this' 
// (2 + 8) * 10 = 200 :) 

或通过显式设置背景:

'use strict'; 

var multiply = function(value) { 
    return value * this.a; 
}; 
var add = function(value) { 
    return value + this.b; 
}; 


var a = 20; 
var b = 8; 

var func = composition2(multiply, add); 

// All the same 
var result1 = this.func(2); 
var result2 = func.call(this, 2); 
var result3 = func.apply(this, [2]); 
+0

我希望这是有道理的,如果它不让我知道。如果需要,我会尝试重新翻译。 – Caramiriel

+0

那么你可以做到这一点,但是你主动反对你正在使用的语言。看看我的回答,为什么做这样的事情没有意义;尽管你可以做到。 – Thomas

+0

我会留下这个答案作为'composition2'实际做的参考,但我同意你的看法。:) – Caramiriel

0

composition1不会通过比第一至g()

其他参数:

var composition1 = function(f, g) { 
    return function(x1, x2, x3) { 
    return f(g(x1, x2, x3)); 
    } 
}; 

功能将前三个参数的工作。如果你想让它为任意数字工作,你需要使用Function.prototype.apply

f.call(...)用于设置this,如Caramiriel的答案中所示。

0

我与作者不同意。

想想功能组成的用例。大多数情况下,我利用变换函数的函数组合(纯函数;参数in,result out和this是无关紧要的)。

2nd。利用arguments他这样做的方式导致了一个糟糕的做法/死胡同,因为它意味着函数g()可能依赖于多个参数。

这意味着,我创建的作品不再可组合,因为它可能无法获得所需的所有参数。
防止合成的成分;失败

(而作为一个副作用:传递的参数对象,以其它任何功能是一种性能不走,因为JS引擎无法优化了)

看看的主题partial application,在JS中通常被误解为currying,这基本上是:除非所有参数都被传递,否则该函数返回另一个函数,该函数接受剩余的参数;直到我有我需要处理它们的所有论据。

然后,您应该重新考虑实现参数顺序的方式,因为当您将它们定义为configs-first,data-last时,它的效果最佳。
实施例:

//a transformer: value in, lowercased string out 
var toLowerCase = function(str){ 
    return String(str).toLowerCase(); 
} 

//the original function expects 3 arguments, 
//two configs and the data to process. 
var replace = curry(function(needle, heystack, str){ 
    return String(str).replace(needle, heystack); 
}); 

//now I pass a partially applied function to map() that only 
//needs the data to process; this is really composable 
arr.map(replace(/\s[A-Z]/g, toLowerCase)); 

//or I create another utility by only applying the first argument 
var replaceWhitespaceWith = replace(/\s+/g); 
//and pass the remaining configs later 
arr.map(replaceWhitespaceWith("-")); 

略微不同的方法是创建那些,通过设计,并不意在获得所有的参数在一个步骤中通过的功能,但一个由一个(或有意义的基团)

var prepend = a => b => String(a) + String(b); //one by one 
var substr = (from, to) => value => String(str).substr(from, to); //or grouped 

arr.map(compose(prepend("foo"), substr(0, 5))); 
arr.map(compose(prepend("bar"), substr(5))); 
//and the `to`-argument is undefined; by intent 

我不打算用所有的参数调用这些函数,我想通过它们的是他们的配置,并获得一个函数来处理传递的数据/值。

而不是substr(0, 5, someString),我总是会写someString.substr(0, 5),所以为什么要努力使最后一个参数(数据)在第一次调用中可用?