JavaScript的那些奇技淫巧

No1.柯里化(currying)与部分应用(partial application)

柯里化(currying)

柯里化是使一个函数

f: X * Y -> R

转变为

f’: X -> (Y -> R)

与用两个参数调用f不同,我们用一个参数运行f’。返回的结果是一个函数,然后用第二个参数调用此函数,得到结果。

如此,如果未柯里化的函数f这样调用

f(3,5)

柯里化后的函数f’是这样调用的

f(3)(5)

比如: 未柯里化的函数add()

1
2
3
4
5
function add(x, y) {
return x + y;
}

add(3, 5); // returns 8

柯里化后的add()

1
2
3
4
5
6
7
function addC(x) {
return function (y) {
return x + y;
}
}

addC(3)(5); // returns 8
柯里化的规则

柯里化将一个二元函数,转变为一元函数,这个函数将返回另一个一元函数。

curry: (X × Y → R) → (X → (Y → R))

Javascript Code:

1
2
3
4
5
6
7
function curry(f) {
return function(x) {
return function(y) {
return f(x, y);
}
}
}
部分应用(partial application)

部分应用将一个函数

f: X * Y -> R

的第一个参数固定而产生一个新的函数

f`: Y -> R

f’与f不同,只需要填写第二个参数,这也是f’比f少一个参数的原因。

比如:将函数add的第一个参数绑定为5来产生函数plus5。

1
2
3
4
5
function plus5(y) {
return 5 + y;
}

plus5(3); // returns 8
部分应用的规则

部分应用使用一个二元函数和一个值产生了一个一元函数。

partApply : ((X × Y → R) × X) → (Y → R)

Javascript Code:

1
2
3
4
5
function partApply(f, x) {
return function(y) {
return f(x, y);
}
}

No2.优化嵌套的条件语句

我们怎样来提高和优化javascript里嵌套的if语句呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
if (color) {
if (color === 'black') {
printBlackBackground();
} else if (color === 'red') {
printRedBackground();
} else if (color === 'blue') {
printBlueBackground();
} else if (color === 'green') {
printGreenBackground();
} else {
printYellowBackground();
}
}

一种方法来提高嵌套的if语句是用switch语句。虽然它不那么啰嗦而且排列整齐,但是并不建议使用它,因为这对于调试错误很困难。这告诉你为什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
switch(color) {
case 'black':
printBlackBackground();
break;
case 'red':
printRedBackground();
break;
case 'blue':
printBlueBackground();
break;
case 'green':
printGreenBackground();
break;
default:
printYellowBackground();
}

如果可以重构的话,我们可以试着简化函数。比如不需要为每个颜色写一个函数,而是将颜色作为函数的参数。

1
2
3
4
5
function printBackground(color) {
if (!color || typeof color !== 'string') {
return;
}
}

但是如果不能重构的话,我们必须避免过多的条件检查,避免过多使用switch。我们必须考虑最有效率的方法,使用object。

1
2
3
4
5
6
7
8
9
10
11
12
let colorObj = {
'black': printBlackBackground,
'red': printRedBackground,
'blue': printBlueBackground,
'green': printGreenBackground,
'yellow': printYellowBackground
};


if (color in colorObj) {
colorObj[color]();
}

No3.运用存储加速递归

大家对斐波那契(Fibonacci)数列都很熟悉。我们可以再20秒内写出下面这样一个方法。

1
2
3
var fibonacci = function(n){
return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
}

它可以运行,但并不高效。它做了太多重复的运算,我们可以通过存储这些运算结果来使其加速。

1
2
3
4
5
6
7
8
9
10
11
var fibonacci = (function() {
var cache = [0, 1]; // cache the value at the n index
return function(n) {
if (cache[n] === undefined) {
for (var i = cache.length; i <= n; ++i) {
cache[i] = cache[i-1] + cache[i-2];
}
}
return cache[n];
}
})()

我们也可以定义一个高阶函数,它接收一个方法作为参数,返回一个该方法运用存储后的新方法。

1
2
3
4
5
6
7
8
var memoize = function(func){
var cache = {};
return function(){
var key = Array.prototype.slice.call(arguments).toString();
return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments));
}
}
fibonacci = memoize(fibonacci);

ES6版本的memoize函数如下:

1
2
3
4
5
6
7
8
var memoize = function(func){
const cache = {};
return (...args) => {
const key = [...args].toString();
return key in cache ? cache[key] : (cache[key] = func(...args));
}
}
fibonacci = memoize(fibonacci);

我们可以将memoize()用在很多其他地方

  • GCD(最大公约数)
1
2
3
4
5
6
7
var gcd = memoize(function(a,b){
var t;
if (a < b) t=b, b=a, a=t;
while(b != 0) t=b, b = a%b, a=t;
return a;
})
gcd(27,183); //=> 3
  • 阶乘运算
1
2
3
4
var factorial = memoize(function(n) {
return (n <= 1) ? 1 : n * factorial(n-1);
})
factorial(5); //=> 120

Original:http://www.jstips.co/

如果想关注最新技术,请关注微信公众号:AutoHome车服务前端团队

坚持原创技术分享,您的支持将鼓励我继续创作!