Skip to content

Latest commit

 

History

History
199 lines (154 loc) · 9.7 KB

File metadata and controls

199 lines (154 loc) · 9.7 KB

一、JavaScript 函数的力量——一个演示

简介

几十年来,函数式编程一直是计算机科学爱好者的宠儿,因其数学上的纯洁性和令人费解的性质而备受推崇,这使得它被隐藏在布满灰尘的计算机实验室中,这些实验室被数据科学家和希望获得博士学位的人占据。但是现在,由于现代语言,比如JuliaRuby****clo jure和——最后但不是最少——JavaScript。

**你说是 JavaScript?网络的脚本语言?没错。

JavaScript 已经被证明是一项重要的技术,它不会消失很长时间。这在很大程度上是因为它能够通过新的框架和库进行重生和扩展,例如****【Jquery】道场下划线. js** 和等等。这直接关系到 JavaScript 作为函数式编程语言的真实身份。对于任何技能水平的程序员来说,理解使用 JavaScript 的函数式编程将是受欢迎的,并且在很长一段时间内都是有用的。**

**为什么这样函数式编程非常强大、健壮、优雅。它在大型数据结构上非常有用和有效。使用 JavaScript(一种客户端脚本语言)作为在日益复杂的网站上操作 DOM、对应用编程接口响应进行排序或执行其他任务的功能性手段可能非常有利。

在这本书里,你将学到所有你需要知道的关于用 JavaScript 进行函数式编程的知识:如何用函数式编程来增强你的 JavaScript web 应用,如何释放 JavaScript 的隐藏能力,以及如何编写更好的代码,既更强大,又——因为它更小——更容易维护,下载速度更快,开销更小。您还将学习函数式编程的核心概念,如何将它们应用于 JavaScript,如何避开使用 JavaScript 作为函数式语言时可能出现的警告和问题,以及如何在 JavaScript 中将函数式编程与面向对象编程混合在一起。

但是在我们开始之前,让我们做一个实验。

演示

也许快速演示将是用 JavaScript 引入函数式编程的最佳方式。我们将使用 JavaScript 执行相同的任务——一次使用传统的本机方法,一次使用函数式编程。然后,我们将比较这两种方法。

应用——一个电子商务网站

为了追求真实世界的应用,假设我们需要一个邮购咖啡豆公司的电子商务网络应用。他们出售几种不同种类和不同数量的咖啡,这两者都会影响价格。

命令式方法

首先,我们走程序路线。为了让这个演示脚踏实地,我们必须创建保存数据的对象。如果我们需要,这允许从数据库中获取值的能力。但是现在,我们假设它们是静态定义的:

// create some objects to store the data.
var columbian = {
  name: 'columbian',
  basePrice: 5
};
var frenchRoast = {
  name: 'french roast',
  basePrice: 8
};
var decaf = {
  name: 'decaf',
  basePrice: 6
};

// we'll use a helper function to calculate the cost 
// according to the size and print it to an HTML list
function printPrice(coffee, size) {
  if (size == 'small') {
    var price = coffee.basePrice + 2;
  }
  else if (size == 'medium') {
    var price = coffee.basePrice + 4;
  }
  else {
    var price = coffee.basePrice + 6;
  }

// create the new html list item
  var node = document.createElement("li");
  var label = coffee.name + ' ' + size;
  var textnode = document.createTextNode(label+' price: $'+price);
  node.appendChild(textnode);
  document.getElementById('products').appendChild(node);
}

// now all we need to do is call the printPrice function
// for every single combination of coffee type and size
printPrice(columbian, 'small');
printPrice(columbian, 'medium');
printPrice(columbian, 'large');
printPrice(frenchRoast, 'small');
printPrice(frenchRoast, 'medium');
printPrice(frenchRoast, 'large');
printPrice(decaf, 'small');
printPrice(decaf, 'medium');
printPrice(decaf, 'large');

类型

下载示例代码

您可以从您在http://www.packtpub.com的账户中下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

可以看到,这个代码非常基础。如果有比我们这里仅有的三种更多的咖啡风格呢?如果有 20 个呢?50?如果除了大小之外,还有有机和非有机的选择呢。这可能会非常快地增加代码行!

使用这种方法,我们告诉机器为每种咖啡类型和每种尺寸打印什么。这是命令式代码的根本问题。

功能编程

命令式代码一步一步地告诉机器需要做什么来解决问题,而函数式编程则试图用数学方法来描述问题,以便机器能够完成剩下的工作。

使用功能更强的方法,同样的应用可以编写如下:

// separate the data and logic from the interface
var printPrice = function(price, label) {
  var node = document.createElement("li");
  var textnode = document.createTextNode(label+' price: $'+price);
  node.appendChild(textnode);
  document.getElementById('products 2').appendChild(node);
}

// create function objects for each type of coffee
var columbian = function(){
  this.name = 'columbian'; 
  this.basePrice = 5;
};
var frenchRoast = function(){
  this.name = 'french roast'; 
  this.basePrice = 8;
};
var decaf = function(){
  this.name = 'decaf'; 
  this.basePrice = 6;
};

// create object literals for the different sizes
var small = {
  getPrice: function(){return this.basePrice + 2},
  getLabel: function(){return this.name + ' small'}
};
var medium = {
  getPrice: function(){return this.basePrice + 4},
  getLabel: function(){return this.name + ' medium'}
};
var large = {
  getPrice: function(){return this.basePrice + 6},
  getLabel: function(){return this.name + ' large'}
};

// put all the coffee types and sizes into arrays
var coffeeTypes = [columbian, frenchRoast, decaf];
var coffeeSizes = [small, medium, large];

// build new objects that are combinations of the above
// and put them into a new array
var coffees = coffeeTypes.reduce(function(previous, current) {
  var newCoffee = coffeeSizes.map(function(mixin) {
    // `plusmix` function for functional mixins, see Ch.7
    var newCoffeeObj = plusMixin(current, mixin);
    return new newCoffeeObj();
  });
  return previous.concat(newCoffee);
},[]);

// we've now defined how to get the price and label for each
// coffee type and size combination, now we can just print them
coffees.forEach(function(coffee){
  printPrice(coffee.getPrice(),coffee.getLabel());
});

首先应该显而易见的是,它更加模块化。这使得添加新尺寸或新咖啡类型变得简单,如下面的代码片段所示:

var peruvian = function(){
  this.name = 'peruvian'; 
  this.basePrice = 11;
};

var extraLarge = {
  getPrice: function(){return this.basePrice + 10},
  getLabel: function(){return this.name + ' extra large'}
};

coffeeTypes.push(Peruvian);
coffeeSizes.push(extraLarge);

咖啡对象和大小对象的数组被“混合”在一起,也就是说,它们的方法和成员变量被组合在一起,带有一个名为plusMixin的自定义函数(参见第 7 章JavaScript 中的函数和面向对象编程)。咖啡类型类包含成员变量,大小包含计算名称和价格的方法。“混合”发生在map操作中,该操作对数组中的每个元素应用一个纯函数,并在reduce()操作中返回一个新函数——另一个类似于map函数的高阶函数,只是数组中的所有元素被组合成一个。最后,用forEach()方法迭代所有可能的类型和大小组合的新数组。forEach()方法是另一个高阶函数,它对数组中的每个对象应用回调函数。在本例中,我们将其作为一个匿名函数提供,该函数实例化对象,并使用对象的getPrice()getLabel()方法作为参数调用printPrice()函数。

实际上,我们可以通过移除coffees变量并将函数链接在一起来使这个例子更加实用——这是函数式编程中的另一个小技巧。

coffeeTypes.reduce(function(previous, current) {
  var newCoffee = coffeeSizes.map(function(mixin) {
    // `plusMixin` function for functional mixins, see Ch.7
    var newCoffeeObj = plusMixin(current, mixin);
    return new newCoffeeObj();
  });
  return previous.concat(newCoffee);
},[]).forEach(function(coffee) {
  printPrice(coffee.getPrice(),coffee.getLabel());
});

此外,控制流程不像命令代码那样从上到下。在函数式编程中,map()函数和其他高阶函数取代了forwhile循环,并且对执行顺序的重视程度很低。这使得新加入范例的人阅读代码变得有点棘手,但是,一旦掌握了窍门,就不难理解了,你会发现它要好得多。

这个例子几乎没有涉及函数式编程在 JavaScript 中能做什么。在这本书里,你会看到功能方法更强大的例子。

总结

首先,采用功能风格的好处显而易见。

第二,不要害怕函数式编程。是的,它经常被认为是计算机语言形式的纯逻辑,但是我们不需要理解λ演算就能够将其应用于日常任务。事实是,通过允许我们的程序被分解成更小的部分,它们更容易理解,更容易维护,也更可靠。map()reduce()函数是 JavaScript 中不太为人所知的内置函数,但我们将看看它们。

JavaScript 是一种脚本语言,交互且平易近人。不需要编译。我们甚至不需要下载任何开发软件,你最喜欢的浏览器可以作为解释器和开发环境。

有兴趣吗?好了,我们开始吧!****