最近我一直在研究很多关于使用Facebook JavaScript库React.js的功能和方法。在谈到它与JavaScript世界其他部分的差异时,通常会提到两种编程样式declarative
和imperative
。
两者之间有什么区别?
答案 0 :(得分:122)
声明式样式,就像反应一样,允许您通过说“它应该看起来像这样”来控制应用程序中的流和状态。一种命令式的风格转过来,让你通过说“这就是你应该做的事情”来控制你的应用程序。
声明性的好处是你不会陷入代表状态的实现细节中。您正在委派保持应用程序视图一致的组织组件,因此您只需担心状态。
想象一下,你有一个管家,他是一个框架的隐喻。你想做晚饭。在一个命令性的世界里,你会告诉他们如何做晚餐。您必须提供以下说明:
Go to the kitchen
Open fridge
Remove chicken from fridge
...
Bring food to the table
在声明世界中,您只需描述您想要的内容
I want dinner with chicken.
如果您的管家不知道如何制作鸡肉,那么您就无法以陈述式进行操作。就像Backbone不知道如何改变自己来完成某项任务一样,你不能只是告诉它去做那项任务。 React能够声明,因为它“知道如何制作鸡”,例如。与Backbone相比,后者只知道如何与厨房接口。
能够描述状态会显着减少虫子的表面积,这是一个好处。另一方面, 事件发生的可能性较小,因为您委派或抽象出如何实现状态。
答案 1 :(得分:57)
想象一个简单的UI组件,例如" Like"按钮。点击它时,如果以前是灰色,则变为蓝色;如果之前是蓝色,则变为灰色。
这样做的必要方法是:
if( user.likes() ) {
if( hasBlue() ) {
removeBlue();
addGrey();
} else {
removeGrey();
addBlue();
}
}
基本上,你必须检查屏幕上当前的内容 处理使用当前状态重绘它所需的所有更改,包括撤消先前状态的更改。你可以想象这在现实场景中会有多复杂。
相反,声明性方法是:
if( this.state.liked ) {
return <blueLike />;
} else {
return <greyLike />;
}
因为声明性方法将关注点分开,所以它的这一部分只需要处理UI在特定状态下的外观,因此更容易理解。
答案 2 :(得分:14)
这是一个很好的类比:
*强制性回应:走出停车场的北出口然后向左走。沿着I-15向南行驶,直到您到达Bangerter高速公路出口。就像你要去宜家一样从出口右转。直行并在第一盏灯右转。继续通过下一个灯,然后在下一个左侧。我的房子是#298。
陈述性回复:我的地址是298 West Immutable Alley,Draper Utah 84020 *
https://tylermcginnis.com/imperative-vs-declarative-programming/
答案 3 :(得分:9)
势在必行的代码:
当强制编写JavaScript代码时,我们会告诉JavaScript究竟该做什么以及如何做。可以把它想象成我们在它应该采取的步骤上给出JavaScript命令。
例如,我给你一个简单的循环:
const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
const excitedPeople = []
for (let i = 0; i < people.length; i++) {
excitedPeople[i] = people[i] + '!'
}
这是必要的代码。我们指导JavaScript在每一步都要做什么。我们必须给它命令:
声明代码:
使用声明性代码,我们不会编写所有步骤以使我们达到最终结果。相反,我们声明了我们想要完成的任务,JavaScript将负责这样做。这个解释有点抽象,所以让我们看一个例子。让我们来看看我们刚刚看到的循环代码的命令,并将其重构为更具声明性。
使用命令式代码,我们执行了所有步骤以获得最终结果。但是,我们真正想要的最终结果是什么?好吧,我们的出发点只是一系列名称:
const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
我们想要的最终目标是一个相同名称的数组,但每个名称都以感叹号结尾:
["Amanda!", "Geoff!", "Michael!", "Richard!", "Ryan!", "Tyler!"]
为了让我们从起点到终点,我们只需使用JavaScript的.map()函数来声明我们想要完成的任务。
const excitedPeople = people.map(name => name + '!')
那就是它!请注意,使用此代码我们还没有:
创建了一个迭代器对象 告诉代码何时应该停止运行 使用迭代器访问people数组中的特定项 将每个新字符串存储在excitedPeople数组中 ......所有这些步骤都由JavaScript的.map()数组方法处理。
答案 4 :(得分:6)
最好比较React(声明性)和JQuery(命令式)来向您展示差异。
在React中,您只需要在render()方法中描述UI的最终状态,而不必担心如何从先前的UI状态转换到新的UI状态。如,
render() {
...
<Label value={this.state.lastPrice} ... />
<Label value={this.state.askPrice} ... />
}
另一方面,JQuery要求您强制转换UI状态,例如选择标签元素并更新其文本。
update() {
...
$("#last-price-label").val(lastPrice);
$("#ask-price-label").val(askPrice);
}
在现实世界的场景中,将会有更多的UI元素需要更新,以及它们的属性(例如,CSS样式和事件监听器)等。如果你使用JQuery强制执行此操作,它将变得复杂和乏味;很容易忘记更新UI的某些部分,或者忘记删除旧的偶数监听器(内存泄漏)等。这就是错误发生的地方,即UI状态和模型状态不同步。
不同步的状态永远不会发生在React的声明性方法中,因为我们只需要更新模型状态,而React负责保持UI和模型状态同步。
您也可以阅读我对What is the difference between declarative and imperative programming?的回答。
PS:从上面的jQuery示例中,您可能会想到如果我们将所有DOM操作放在update()方法中,并且每当我们的模型状态发生任何变化时都会调用它,并且我的UI永远不会不同步。你是对的,这实际上是React render()所做的,唯一的区别是jQuery update()将导致许多不必要的DOM操作,但React将仅使用其Virtual DOM Diffing Algorithm更新已更改的DOM元素。答案 5 :(得分:1)
这是我目前的理解:
声明性代码(几乎?)始终是代码之上的抽象层,本质上更具有命令性。
React 允许您编写声明性代码,该代码是直接与 DOM 交互的命令式代码(例如 the diffing algorithm)之上的抽象层。如果您需要编写命令式代码(即直接与 DOM 交互),React 提供了 Refs as an escape hatch。
答案 6 :(得分:1)
声明式编程是一种编程风格,其中应用程序以优先级的方式构建 描述应该发生什么,而不是定义它应该如何发生。
为了理解声明式编程,让我们将它与命令式编程(编程风格只是 关心如何用代码实现结果)。
示例:使字符串对 URL 友好。通常,这可以通过用连字符替换字符串中的所有空格来实现, 因为空格不是 URL 友好的。首先,此任务的命令式方法:
const string = "difference between declarative and imperative in react.js";
const urlFriendly = "";
for (var i = 0; i < string.length; i++) {
if (string[i] === " ") {
urlFriendly += "-";
} else {
urlFriendly += string[i];
}
}
console.log(urlFriendly); // "difference-between-declarative-and-imperative-in-react-js"
在本例中,我们遍历字符串中的每个字符,并在出现空格时替换它们。这个程序的结构只关心如何完成这样的任务。我们使用 for 循环和 if 语句并使用相等运算符设置值。只是 仅查看代码并不能告诉我们很多信息,因为命令式程序需要大量注释才能了解发生了什么。
现在让我们看一下针对同一问题的声明式方法:
const string = "Difference between declarative and imperative in React.js?";
const urlFriendly = string.replace(/ /g, "-");
console.log(urlFriendly);
这里我们使用 string.replace
和一个正则表达式来
用连字符替换所有空格实例。使用 string.replace
是
一种描述应该发生的事情的方式:字符串中的空格
应该更换。如何处理空间的细节是
在替换函数内部抽象出来。
在声明式程序中,语法本身描述应该发生的事情,并且抽象出事情如何发生的细节。
本质上,声明式编程生成的应用程序更易于推理,当应用程序更易于推理时,该应用程序更易于扩展。有关声明式编程范式的其他详细信息,请访问 Declarative Programming wiki。
现在,让我们考虑构建文档对象模型的任务。命令式方法将关注 DOM 的构造方式:
const target = document.getElementById("target");
const wrapper = document.createElement("div");
const headline = document.createElement("h1");
wrapper.id = "welcome";
headline.innerText = "Hello World";
wrapper.appendChild(headline);
target.appendChild(wrapper);
此代码涉及创建元素、设置元素和 将它们添加到文档中。很难做出改变, 添加功能,或在 DOM 所在的位置扩展 10,000 行代码 命令式构造。
现在让我们来看看如何以声明方式构建 DOM 使用 React 组件:
const { render } = ReactDOM;
const Welcome = () => (
<div id="welcome">
<h1>Hello World</h1>
</div>
);
render(<Welcome />, document.getElementById("target"));
React 是声明式的。在这里,Welcome 组件描述了 DOM 应该被渲染。渲染函数使用指令 在组件中声明以构建 DOM,抽象掉 有关如何呈现 DOM 的详细信息。我们可以清楚地看到,我们 想要将我们的 Welcome 组件渲染到 ID 为 目标
答案 7 :(得分:0)
答案 8 :(得分:0)
命令性代码指示JavaScript如何执行每个步骤。使用声明性代码,我们可以告诉JavaScript我们要做的事情,然后让JavaScript负责执行这些步骤。
React是声明性的,因为我们编写了所需的代码,而React负责获取声明的代码并执行所有JavaScript / DOM步骤以使我们获得所需的结果。
答案 9 :(得分:0)
我先做个比喻:我有两辆车,在我的两辆车中,我希望车内的温度为正常的室温〜72°F。在第一辆(较旧的)汽车中,有两个用于控制温度的旋钮(一个用于控制温度的旋钮和一个用于控制气流的旋钮)。当温度过高时,我必须调节第一个旋钮以降低温度,并可能改变气流),反之则过冷。这是当务之急!我必须自己控制旋钮。在我的第二辆(较新的)汽车中,我可以设置/声明温度。这意味着我不必费力调节旋钮即可调节温度,因为我知道汽车声明/将温度设置为72°F,并且汽车将必须完成该状态。
React是相同的,您声明标记/模板和stat,然后React进行必要的工作以使DOM与您的应用保持同步。
<button onClick={activateTeleporter}>Activate Teleporter</button>
我们声明所需的内容,而不是使用.addEventListener()
来设置事件处理。单击该按钮后,它将运行activateTeleporter
函数。
答案 10 :(得分:0)
命令式世界中的现实生活将进入啤酒酒吧,并向调酒师提供以下说明: -从架子上拿杯 -将玻璃杯放在草稿的前面 -拉下把手直到玻璃杯装满 -把玻璃杯递给我。
相反,在声明性世界中,您只会说:“请啤酒。”
以声明式方式询问啤酒的假设是,酒保知道如何提供啤酒,这是声明式编程工作方式的重要方面。
在声明式编程中,开发人员仅描述他们想要实现的目标,因此无需列出所有步骤即可使之工作。
React提供声明性方法的事实使得它易于使用,因此,生成的代码很简单,这通常导致更少的错误和更高的可维护性。
由于React遵循声明式范例,因此无需告诉它如何与DOM交互;您只需声明要在屏幕上看到的内容,然后React就会为您完成工作。
答案 11 :(得分:0)
声明性编程是一种编程范式…,它表示计算的逻辑而不描述其控制流程。
命令式编程是一种编程范式,它使用可更改程序状态的语句。
参考链接:-https://codeburst.io/declarative-vs-imperative-programming-a8a7c93d9ad2
答案 12 :(得分:0)
声明式与命令式
声明式编程就像要求您的朋友粉刷您的房屋。您不在乎他们如何清洁它,他们用来绘画的颜色,他们用来完成它的资源有多少。”
//Declarative For Searching element from an array
array.find(item)
与声明式相反的是必须的。命令式方法的一个常见示例是 你告诉你的朋友到底该怎么做给房子粉刷吗?
//命令式算法
def imperative_search(array, item)
for i in array do
if i == item
return item
end
end
return false
end
答案 13 :(得分:0)
解释每一步是势在必行的方法,例如我们必须用 Hello World 创建段落标签!里面的文字。
//Imperative
const para = document.createElement('p');
para.innerText = 'Hello World !';
document.querySelector('#root').appendChild(para);
定义所需的最终目标状态而不指定确切的程序。即带有 text 的 p 标签,不告诉 createElement 或 innerText
//Delcartive
import React from "react";
import ReactDOM from "react-dom";
const App = () =>{
return(<p>Hello World !</p>);
}
ReactDOM.render(<App />, document.getElementById("root"));