声明式编程和命令式编程
⼀、声明式编程与命令式编程
  先统⼀⼀下概念,我们有两种编程⽅式:命令式和声明式。我们可以像下⾯这样定义它们之间的不同:
命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
⼆、声明式编程和命令式编程案例
  最简单的例⼦就是,如果要实现页⾯的跳转的功能:
  使⽤a标签:<a href="/finance" />,就是声明式编程。
  使⽤js代码:window.location.href = "/finance",就是命令式编程。
  同样实现⼀个功能,a标签只提供了跳转的路径,跳转的动作是约定好的,⽽js代码则要写明怎么跳转,⽤什么对象跳转。
  再举个简单的例⼦,假设我们想让⼀个数组⾥的数值翻倍。
  我们⽤命令式编程风格实现,像下⾯这样(JavaScript):
1var numbers = [1,2,3,4,5]
2
3var doubled = []
4
5for(var i = 0; i < numbers.length; i++) {
6
7var newNumber = numbers[i] * 2
8  doubled.push(newNumber)
9
10}
11 console.log(doubled) //=> [2,4,6,8,10]
  我们直接遍历整个数组,取出每个元素,乘以⼆,然后把翻倍后的值放⼊新数组,每次都要操作这个双倍数组,直到计算完所有元素。  ⽽使⽤声明式编程⽅法,我们可以⽤Array.map函数,像下⾯这样(JavaScript):
1var numbers = [1,2,3,4,5]
2
3var doubled = numbers.map(function(n) {
4
5return n * 2
6})
7 console.log(doubled) //=> [2,4,6,8,10]
  map利⽤当前的数组创建了⼀个新数组,新数组⾥的每个元素都是经过了传⼊map的函数(这⾥是function(n) { return n*2 })的处理。
  map函数所作的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传⼊map的是⼀个纯函数;它不具有任何副作⽤(不会改变外部状态),它只是接收⼀个数字,返回乘以⼆后的值。
  在⼀些具有函数式编程特征的语⾔⾥,对于list数据类型的操作,还有⼀些其他常⽤的声明式的函数⽅法。例如,求⼀个list⾥所有值的和,命令式编程会这样做(JavaScript):
1var numbers = [1,2,3,4,5]
2
3var total = 0
4
5for(var i = 0; i < numbers.length; i++) {
6
7  total += numbers[i]
8}
9 console.log(total) //=> 15
  ⽽在声明式编程⽅式⾥,我们使⽤reduce函数(JavaScript):
1var numbers = [1,2,3,4,5]
2
3var total = duce(function(sum, n) {
4
5return sum + n
6});
7 console.log(total) //=> 15
svg交互是什么
  reduce函数利⽤传⼊的函数把⼀个list 运算成⼀个值。它以这个函数为参数,数组⾥的每个元素都要经过它的处理。每⼀次调⽤,第⼀个参数(这⾥是sum)都是这个函数处理前⼀个值时返回的结果,⽽第⼆个参数(n)就是当前元素。这样下来,每此处理的新元素都会合计到sum中,最终我们得到的是整个数组的和。
  同样,reduce函数归纳抽离了我们如何遍历数组和状态管理部分的实现,提供给我们⼀个通⽤的⽅式来把⼀个list 合并成⼀个值。我们需要做的只是指明我们想要的是什么?
三,⾛进声明式编程
  如果你之前没有听说过map和reduce函数,你的第⼀感觉,我相信,就会是这样。作为程序员,我们⾮常习惯去指出事情应该如何运⾏。“去遍历这个list”,“if 这种情况then 那样做”,“把这个新值赋给这个变量”。当我们已经知道了如何告诉机器该如何做事时,为什么我们需要去学习这种看起来有些怪异的归纳抽离出来的函数⼯具?
  在很多情况中,命令式编程很好⽤。当我们写业务逻辑,我们通常必须要写命令式代码,没有可能在我们的专项业务⾥也存在⼀个可以归纳抽离的实现。
  但是,如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨⼤的
便捷。⾸先,我可以少写代码,这就是通往成功的捷径。⽽且它们能让我们站在更⾼的层⾯是思考,站在云端思考我们想要的是什么,⽽不是站在泥⾥思考事情该如何去做。
声明式编程语⾔:SQL
  也许你还不能明⽩,但有⼀个地⽅,你也许已经⽤到了声明式编程,那就是SQL。
  你可以把SQL当做⼀个处理数据的声明式查询语⾔。完全⽤SQL写⼀个应⽤程序?这不可能。但如果是处理相互关联的数据集,它就显的⽆⽐强⼤了。
  像下⾯这样的查询语句:
  SELECT * from dogs INNER JOIN owners WHERE dogs.owner_id = owners.id
  如果我们⽤命令式编程⽅式实现这段逻辑(JavaScript):
1//dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ]
2//owners = [{id: 1, name: 'Bob'}, {...}, ...]
3
4var dogsWithOwners = []
5var dog, owner
6
7for(var di=0; di < dogs.length; di++) {
8
9  dog = dogs[di]
10
11for(var oi=0; oi < owners.length; oi++) {
12
13    owner = owners[oi]
14if (owner && dog.owner_id == owner.id) {
15
16      dogsWithOwners.push({
17        dog: dog,
18        owner: owner
19
20      })
21    }
22  }}
23 }
  我可没说SQL是⼀种很容易懂的语⾔,也没说⼀眼就能把它们看明⽩,但基本上还是很整洁的。
  SQL代码不仅很短,不不仅容易读懂,它还有更⼤的优势。因为我们归纳抽离了how,我们就可以专注于what,让数据库来帮我们优化how.
  我们的命令式编程代码会运⾏的很慢,因为需要遍历所有 list ⾥的每个狗的主⼈。
  ⽽SQL例⼦⾥我们可以让数据库来处理how,来替我们去我们想要的数据。如果需要⽤到索引(假设我们建了索引),数据库知道如何使⽤索引,这样性能⼜有了⼤的提升。如果在此不久之前它执⾏过相同的查询,它也许会从缓存⾥⽴即到。通过放⼿how,让机器来做这些有难度的事,我们不需要掌握数据库原理就能轻松的完成任务。
声明式编程:d3.js
  另外⼀个能体现出声明式编程的真正强⼤之处地⽅是⽤户界⾯、图形、动画编程。
  开发⽤户界⾯是有难度的事。因为有⽤户交互,我们希望能创建漂亮的动态⽤户交互⽅式,通常我们会⽤到⼤量的状态声明和很多相同作⽤的代码,这些代码实际上是可以归纳提炼出来的。
  d3.js ⾥⾯⼀个⾮常好的声明时归纳提炼的例⼦就是它的⼀个⼯具包,能够帮助我们使⽤JavaScript和SVG来开发交互的和动画的数据可视化模型。
  第⼀次(或第5次,甚⾄第10次)你开发d3程序时可能会头⼤。跟SQL⼀样,d3是⼀种可视化数据操作的强⼤通⽤⼯具,它能提供你所有how⽅法,让你只需要说出你想要什么。
  下⾯是⼀个例⼦。这是⼀个d3可视化实现,它为data数组⾥的每个对象画⼀个圆。为了演⽰这个过程,我们每秒增加⼀个圆。
  ⾥⾯最有趣的⼀段代码是(JavaScript):
1//var data = [{x: 5, y: 10}, {x: 20, y: 5}]
2
3var circles = svg.selectAll('circle')
4
5                    .data(data)
6
().append('circle')
8
9            .attr('cx', function(d) { return d.x })
10
11            .attr('cy', function(d) { return d.y })
12
13            .attr('r', 0)
14        .transition().duration(500)
15
16          .attr('r', 5)
  没有必要完全理解这段代码都⼲了什么(你需要⼀段时间去领会),但关键点是:
  ⾸先我们收集了svg⾥所有的圆,然后把data数组数据绑定到对象⾥。
  D3 对每个圆都绑定了那些点数据有⼀个关系表。最初我们只有两个点,没有圆,我们使⽤.enter()⽅法获取数据点。这⾥,我们的意图是画⼀个圆,中⼼是x和y,初始值是0,半秒后变换成半径为5。
四、声明式编程的优势
  从头再看⼀遍代码,想⼀想,我们是在声明我们想要的图案是什么样⼦,还是在说如何作图。你会发现这⾥根本没有关于how的代码。我们只是在⼀个相当⾼的层⾯描述我们想要的是什么:
我要画圆,圆⼼在data数据⾥,当增加新圆时,⽤动画表⽰半径的增加。
  这太神奇了,我们没有写任何循环,这⾥没有状态管理。画图操作通常是很难写,很⿇烦,很让⼈讨厌,但这⾥,d3归纳提取了⼀些常⽤的操作,让我们专注于描述我们想要的是什么。
  现在再看,d3.js 很容易理解吗?不是,它绝对需要你花⼀段时间去学习。⽽学习的过程基本上需要你放弃去指明如何做事的习惯,⽽去学会如何描述我想要的是什么。
  最初,这可能是很困难的事,但经过⼀些时间的学习后,⼀些神奇的事情发⽣了——你变得⾮常⾮常有效率了。通过归纳提取
how,d3.js 能让你真正的专注说明你想要看到的是什么,让你在⼀个个更⾼的层⾯解决问题,解放你的创作⼒。
  声明式编程让我们去描述我们想要的是什么,让底层的软件/计算机/等去解决如何去实现它们。
  在很多情况中,就像我们看到的⼀样,声明式编程能给我们的编程带来真正的提升,通过站在更⾼层⾯写代码,我们可以更多的专注于what,⽽这正是我们开发软件真正的⽬标。
  问题是,程序员习惯了去描述how,这让我们感觉很好很舒服——强⼒——能够控制事情的发⽣发展,不放⾛任何我们不能看见不能理解的处理过程。
  有时候这种紧盯着how不放的做法是没问题的。如果我需要对代码进⾏更⾼性能的优化,我需要对what进⾏更深⼀步的描述来指导how。有时候对于某个业务逻辑没有任何可以归纳提取的通⽤实现,我们只能写命令式编程代码。
  但⼤多数时候,我们可以、⽽且应该寻求声明式的写代码⽅式,如果没有发现现成的归纳提取好的实现,我们应该⾃⼰去创建。起初这会很难,必定的,但就像我们使⽤SQL和D3.js,我们会长期从中获得巨⼤的回报!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。