为什么Java世界中的一个人成为Node.js和JavaScript的热心支持者?

我们今天出版的材料的作者David Harron提出了以下问题:“一个人在Java SE团队的Sun Microsystems工作了10年以上,直到最后一口气,才应该只考虑Java字节码并创建抽象接口的实例吗? ”。 他问了一个关于自己的问题,对他来说,在Java之后,Node.js平台变得像新鲜空气一样。 David说,他在2009年1月被Sun解雇时(就在收购这家Oracle公司之前),他发现了有关Node.js的信息。 这项技术吸引了他。 “钩”是什么意思? 自2010年以来,他撰写了许多有关Node.js编程的文章。 即,他写了几本书,包括Node.js Web Development,该书的第四版已于今年发行。 他准备了许多在Internet上发布的有关Node.js的小材料。 实际上,他花了大量时间和精力谈论Node.js平台和JavaScript功能。 为什么以前使用Java专门工作的人被Node.js和JavaScript如此吸引?

图片

关于Java世界


在Sun工作期间,我坚信Java技术。 我用JavaONE进行了演示,参与了java.awt.Robot类的开发,组织了Mustang回归竞赛活动(这是一个旨在查找Java 1.6中的错误的竞赛),帮助启动了Java发行许可证,这是一个答案。 OpenJDK出现之前有关Linux JDK发行版的问题。 后来,我在启动OpenJDK项目中发挥了作用。 在大约6年的时间里,我在java.net上发布了博客材料(现在该网站已关闭)。 每周有1-2篇文章专门讨论Java生态系统中的重大事件。 保护Java不受那些预言该技术前景黯淡的人的影响,在我的工作中发挥了重要作用。


该奖项,杜克奖,授予了Sun最杰出的员工。 我组织了野马回归大赛之后才知道

做了很多与Java相关的事情的人怎么了? 实际上,在这里我想谈一谈我如何从Java爱好者变成Node.js和JavaScript的热心支持者。

我必须说,发生在我身上的事情不能被称为完全放弃Java。 在过去的三年中,我已经使用Spring和Hibernate编写了很多Java代码。 尽管我现在很喜欢这个领域的工作(例如,我在太阳能行业工作,喜欢做我喜欢做的事情-我写请求处理来自能源部门的数据),但我现在却在看Java编程失去了昔日的辉煌。

使用Spring进行了两年的开发,使我可以清楚地阐明一个重要的事情:隐藏复杂机制的尝试不会带来简单性,而只会导致出现更复杂的结构。

简而言之,这是我将在本材料中谈到的主要思想:

  • Java程序充满了样板代码,隐藏了程序员的意图。
  • 使用Spring和Spring Boot给了我一个很好的教训,那就是试图隐藏复杂的机制会导致更加复杂的构造。
  • 可以说,Java EE平台是一个通过“共同努力”创建的项目,它绝对涵盖了企业应用程序开发的所有需求。 结果,Java EE平台被证明是禁止的。
  • 在某种程度上,使用Spring进行开发是一种愉快的体验。 当您从未听说过的子系统的深处出现完全无法理解的异常时,这种幻想就消失了,而且至少需要三天的时间才能找出问题所在。
  • 您需要什么样的辅助机制在系统上造成过大的负担?您需要一个可以为程序员“编写”代码的框架吗?
  • 尽管像Eclipse这样的IDE是功能强大的应用程序,但它们可以指示Java生态系统的复杂性。
  • Node.js平台的产生是由于一个人努力改善其对轻量级事件驱动架构的了解。
  • JavaScript社区似乎热衷于摆脱样板代码,这使程序员可以尽可能清楚地表达自己的意图。
  • Async / await是JS回调地狱问题的一种解决方案,它是拒绝模板代码的示例,有助于清晰地表达程序员的意图。
  • 为Node.js编程是一种真正的乐趣。
  • JavaScript中没有特定于Java的强类型。 这是舌头的祝福和诅咒。 这使编写代码变得更加容易,但是为了验证其正确性,您必须花费更多的时间进行测试。
  • npm / yarn引入的卷装管理系统简单易用。 她无法与Maven相提并论。
  • Java和Node.js均提供出色的性能。 这与JavaScript是一种慢速语言的说法背道而驰,JavaScript是一种慢速语言,使用它会导致Node.js平台的性能下降。
  • 性能Node.js建立在Google改进V8的努力的基础上,该引擎会影响Chrome浏览器的速度。
  • 基于浏览器的JS引擎制造商之间的激烈竞争为JavaScript的发展做出了贡献,这对Node.js十分有利。

关于Java开发问题


有些工具或对象是工程师经过多年努力改进的结果。 程序员尝试了不同的想法,删除了不必要的属性,结果他们得到的实体中确实有解决某个问题所需的东西。 通常,此类技术具有隐藏了强大功能的非常吸引人的简单性。 这不适用于Java。

Spring是用于开发基于Java的Web应用程序的流行框架。

Spring(特别是Spring Boot)的主要目标是提供使用预配置的Java EE堆栈的功能。 使用Spring的程序员不应为了创建现成的系统而照顾servlet,持久性存储系统,应用程序服务器以及未知的事物。 所有这些问题都传递给了Spring,程序员正在编写实现应用程序逻辑的代码。 例如,JPARepository机制负责为名称类似于findUserByFirstName方法生成数据库查询。 程序员不必为此类方法编写代码。 只需将方法的描述传递给系统即可,其余的工作将由Spring完成。

听起来一切都很好,以这种风格进行工作很不错,但是-直到出现意外情况为止。

我的意思是这样一种情况,例如,将Hibernate PersistentObjectException抛出,并将消息detached entity passed to persist 。 什么意思 花了好几天才找到答案。 事实证明,如果以非常简化的方式描述所有内容,则意味着在REST端点接收的JSON数据具有带有某些值的ID字段。 再次,休眠,如果不详细介绍,则试图控制ID值,并因此引发上述晦涩的异常。 成千上万的此类错误消息令人困惑且难以阅读。 考虑到在Spring中有相互依存的子系统的整个级联,Spring堆栈看起来像是程序员的发誓敌人,看着他,等待程序员犯下最小的错误,当这种错误发生时,抛出与其不兼容的异常应用程序的正常运行。

此外,在这里您可以调用最长的堆栈跟踪。 它们代表了充满各种抽象方法的几个屏幕。 Spring显然创建了实现代码中所表达内容所必需的配置。 毫无疑问,这样的抽象水平需要大量的辅助逻辑,其目的是发现代码工作所需的一切,例如为了满足请求。 而且长堆栈跟踪不一定很糟糕。 这些事情很可能是一种症状,从而引发了由辅助机制在系统上造成什么样的负载的问题。

假设程序员没有为这种方法编写代码,那么如何findUserByFirstName方法? 该框架需要解析方法名称,了解程序员的意图,创建诸如抽象语法树之类的东西,生成某种SQL代码,等等。 所有这些如何加载系统? 所有这一切仅存在,以便程序员无需编写代码?

在经过数十次搜索类似上述错误的含义的工作之后,花了几周的时间来尝试揭露通常不应该揭开的秘密,您可以得出我得出的相同结论。 其含义是,隐藏复杂机制的尝试不会导致简单化,而只会导致出现甚至更复杂的结构。 Node.js平台要简单得多。

口号“兼容性问题”隐藏了一个绝妙的主意,根据该想法,向后兼容性是Java平台最重要的功能。 我们认真对待这一点,将图像放在T恤上,如下图所示。


向后兼容性非常重要。

当然,对后向兼容性的这种关注可能会引起持续的焦虑,并且不时地摆脱不再有用的旧机制很有用。

Java和Node.js


Spring和Java EE过于复杂。 在其背景下的Node.js平台被视为新鲜空气。 当您熟悉Node.js时,您会注意到的第一件事是Ryan Dahl进行平台核心开发的方法。 他的经验告诉他,使用流的平台是创建复杂的重量级系统所必需的。 他正在寻找其他东西,并花了几年时间来完善Node.js中体现的一组基本机制。 结果,他得到了一个轻量级的系统,该系统的特征在于单执行线程,创造性地使用匿名JavaScript函数作为异步回调以及一个最初实现异步机制的运行时库。 创建这样的系统时,最初的消息是在回调函数中提供高性能事件处理以及这些事件的传递。

接下来,Node.js的一个重要功能是使用JavaScript。 有一种感觉,那些用JS编写的人倾向于摆脱模板代码,这使他们可以清楚地描述程序员的意图。

作为Java和JavaScript之间差异的示例,请考虑实现侦听器功能(观察者)。 在Java中,要与侦听器一起使用,您需要创建抽象接口的特定实例。 这需要使用笨拙的语言构造来隐藏正在发生的事情的本质。 如何识别隐藏在样板代码掩盖下的程序员的意图?

JavaScript使用简单的匿名函数代替。 实现侦听器时,无需寻找合适的抽象接口。 无需使用各种辅助文本即可编写必要的代码就足够了。

因此,可以通过对上述机制的分析得出一个重要的想法:大多数编程语言都隐藏了程序员的意图,这导致了代码难以理解的事实。

关于使用Node.js提供的回调函数的决定看起来很有吸引力。 但这并非没有问题。

解决问题和解决问题


在JavaScript中,与异步编程相关的问题长期以来一直存在两个问题。 首先是Node.js所谓的回调地狱。 这个问题在于,在开发过程中,很容易陷入由深层嵌套的回调函数建立的陷阱,其中嵌套的每个级别使程序复杂,并处理代码和错误的结果。 与此相关的另一个问题是JavaScript语言机制不能帮助程序员正确表达异步代码执行的思想。

已经出现了一些库来简化JS上的异步开发。 但这是试图隐藏复杂机制的另一个例子,这只会导致出现甚至更复杂的结构。

考虑一个例子:

 const async = require('async'); const fs = require('fs'); const cat = function(filez, fini) { async.eachSeries(filez, function(filenm, next) {   fs.readFile(filenm, 'utf8', function(err, data) {     if (err) return next(err);     process.stdout.write(data, 'utf8', function(err) {       if (err) next(err);       else next();     });   }); }, function(err) {   if (err) fini(err);   else fini(); }); }; cat(process.argv.slice(2), function(err) { if (err) console.error(err.stack); }); 

这是Unix cat的非描述性模仿。 asyncasync简化异步调用序列。 但是,其使用需要大量的样板代码,这隐藏了程序员的意图。

本质上,此代码包含一个循环。 它不是作为常规循环编写的;它没有使用循环描述的自然构造。 此外,代码执行的结果和它们所产生的错误不会到达正确的位置。 它们被锁定在回调中,这很不方便。 但是,在Node.js中实施ES2015 / 2016标准之前,没有更好的办法可以做。

如果我们考虑到新功能(尤其是Node.js 10.x中提供的新功能)重写此代码,则会得到以下信息:

 const fs = require('fs').promises; async function cat(filenmz) { for (var filenm of filenmz) {   let data = await fs.readFile(filenm, 'utf8');   await new Promise((resolve, reject) => {     process.stdout.write(data, 'utf8', (err) => {       if (err) reject(err);       else resolve();     });   }); } } cat(process.argv.slice(2)).catch(err => {   console.error(err.stack); }); 

在此示例中,我们使用了async/await构造。 这里显示的异步机制与前面的示例相同,但是这里使用了组织循环中使用的常用结构。 处理结果和错误看起来很正常。 这样的代码更容易读写。 这种方法很容易理解程序员的意图。

唯一的缺点是process.stdout.write没有Promise接口,因此,如果不将其包装在promise中,则不能在异步函数中使用此机制。

现在我们可以得出结论,JavaScript中的回调地狱问题的解决方式不同于尝试隐藏复杂的机制。 相反,对语言进行了更改,从而解决了问题本身,并使我们免于由于在临时解决方案中使用大量模板代码而造成的不便。 另外,通过使用异步/等待机制,代码变得更加美丽。

在本节中,我们首先讨论了Node.js的缺陷,但是对回调地狱的一个很好的解决方案导致对缺陷的讨论变成了对Node.js和JavaScript优势的讨论。

强大的打字,界面和虚构的代码清晰度


在那些保护Java免受各种攻击的日子里,我强调严格的类型允许您编写大型应用程序。 那时,正在使用单片系统的开发(没有微服务,没有Docker等)。 由于Java是一种强类型语言,因此Java编译器可以防止程序员编译错误的代码,从而帮助程序员避免了很多问题。

与Java不同,JavaScript不是强类型的。 由此我们可以得出一个明显的结论,即程序员并不确切知道他必须使用哪些对象。 例如,程序员如何才能知道如何处理从某处收到的某个对象?

强大的Java输入的另一面是,您需要不断执行样板操作。 程序员一直在强制转换或检查所有内容是否完全符合预期。 开发人员花费时间编写代码,以极高的准确性完成代码,使用了大量的模板设计,并希望所有这些将有助于他通过及早发现和纠正错误来节省时间。

使用强类型语言进行编程的问题非常严重,以至于几乎没有任何选择的程序员必须使用大型,复杂的IDE。 一个简单的代码编辑器在这里还不够。 使Java程序员保持适当状态(披萨除外)的唯一方法是不断向他显示包含可用对象字段或方法参数描述的下拉列表。 IDE,Eclipse,NetBeans或IntelliJ之类的IDE的这种和其他支持机制有助于创建类,促进重构和其他任务。

而且...我不会谈论Maven。 这只是一个噩梦工具。

在JavaScript中,在声明变量类型时未指定它们,通常不使用类型转换,依此类推。 结果,代码更易于阅读,但是这种状态也意味着存在难以检测到的编程错误的风险。

前述内容与Java的优点有关还是与缺点有关,取决于观点。

十年前,我相信所有这些困难都可以通过使程序员对他编写的代码更有信心来证明自己是对的。 今天,我相信强类型会增加程序员的工作量,并且像使用JavaScript一样开发项目要容易得多。

使用易于测试的小型模块解决错误


Node.js促使程序员将其项目分解为小片段,分为所谓的模块。 您可能会发现此事实无关紧要,但部分解决了我们刚才提到的问题。

以下是模块的主要特征:

  • 独立性 该模块将互连的代码合并为一个实体。
  • 明确界限。 保护模块内部的代码不受任何外部机制的干扰。
  • 明确出口。 默认情况下,不导出代码和模块数据。 开发人员独立决定哪些功能和数据需要公开提供。
  • 显式导入。 在开发模块时,程序员自己决定他将依赖哪个模块。
  • 潜在的独立性。 从广义上讲,可以通过在npm中发布模块来公开提供模块,或者,如果模块是为公司内部需求而设计的,则可以在封闭的存储库中发布。 这使得在不同的应用程序中使用相同的模块变得容易。
  • 易于理解的代码。 模块小巧,简化了对其代码的阅读和理解,为自由讨论它们提供了可能性。
  • 方便测试。 如果正确实施了一个小模块,则可以轻松进行单元测试。

所有这些使Node.js模块实体具有明确定义的边界,其代码易于编写,阅读和测试。

但是,担心使用JavaScript是一个事实,即缺少强类型输入很容易导致代码做错事情。 在一个旨在解决具有清晰边界的狭窄问题的小型模块中,“某些地方出了问题”仅会影响模块本身的代码。 这导致一个事实,即可能导致缺乏严格键入的问题被锁定在模块中。

解决JavaScript动态类型问题的另一种方法是彻底测试代码。

开发人员必须认真对待测试,这剥夺了JS开发过程的简单性带来的部分好处。 由JS程序员创建的测试系统应该发现那些错误,如果由JS程序员以Java之类的方式开发,则编译器会自动发现这些错误。 您是否正在为JS应用程序编写测试?

对于那些需要使用JavaScript进行静态输入的系统,最好看一下TypeScript。 我没有使用这种语言,但是我听到了很多关于它的好东西。 它与JavaScript兼容,并通过类型控制系统和其他有用功能扩展了语言。

最后,我们可以说使用模块化方法进行开发是Node.js和JavaScript的优势。

包装管理


我对Maven的想法感到很难过,所以我什至不能正常地写它。 而且,据我了解,Maven毫不妥协地被爱或恨。

这里的问题是在Java环境中没有用于管理软件包的整体系统。 Maven软件包存在,您可以正常使用它们,而Gradle支持它们。 但是,与他们一起工作的组织方式与Node.js的包管理系统为开发人员带来的便利性并不十分相似。

在Node.js的世界中,有两个很棒的程序包管理器可以紧密合作。 首先,唯一的此类工具是npm存储库和同名的命令行工具。

感谢npm,我们有一个描述包依赖关系的优秀方案。 依赖关系可以是严格的(例如,表明仅需要某个程序包的1.2.3版本),也可以具有多个自由度-最高* ,这意味着使用某个程序包的最新版本。

Node.js社区已在npm存储库中发布了数十万个软件包。 同时,使用不在npm中的软件包与使用npm中的软件包一样容易。

事实证明,npm系统非常成功,以至于Node.js上服务器产品的开发人员不仅可以使用它,而且前端程序员也可以使用它。 以前,使用Bower之类的工具来管理软件包。 不推荐使用Bower,现在您可以发现所有用于前端开发的JS库都以npm软件包的形式存在。 许多用于客户端开发的支持工具,例如Vue.js CLI和Webpack,都是作为Node.js应用程序编写的。

另一个用于Node.js的软件包管理系统yarn可以从npm存储库中下载软件包,并使用相同的配置文件。 与npm软件包管理器相比,纱线的主要优点是速度更高。

无论使用npm软件包管理器还是yarn软件包管理器,npm存储库都是使Node.js开发变得如此轻松和愉快的强大基础。


有一次,在我帮助开发java.awt.Robot之后,我受到启发去创建这个东西。 虽然官方的Duke图像是由曲线组成的,但RoboDuke是从直线构建的。 仅此机器人的肘关节是圆形的

性能表现


Java和JavaScript都因性能不佳而受到批评。在这两种情况下,编译器都将程序的源代码转换为在为特定平台实现的虚拟机上执行的字节码。反过来,虚拟机使用各种优化将字节码转换为机器码。

Java和JavaScript都有追求高性能的理由。如果我们谈论Java和Node.js,那么它们与对快速服务器代码的渴望联系在一起。对于基于浏览器的JavaScript,获得高性能的动力在于提高客户端应用程序的质量。我们将在有关富Internet应用程序的部分中讨论这一点。

JDK Sun/Oracle HotSpot — , -. , , , , , . HotSpot — , .

JavaScript, , JS-, , , - . , JavaScript . , , . , , Google Docs, . JS .

Node.js , V8 Google Chrome.

, Google, V8, . , V8 Crankshaft Turbofan.

— , , R Python. , , . JavaScript, , , JavaScript.

JavaScript , TensorFlow.js. API API TensorFlow Python, . , , , .

IBM, Node.js, , , Docker/Kubernetes. , Node.js Spring Boot. -, . , Node.js , , , V8.

, Node.js . . - , Node.js , . , «Node.js Web Development», , :

  • — .
  • , Node.js .
  • .

JavaScript , Node.js. — Node.js-. Node.js- node-gyp , . , Rust- Node.js.

WebAssembly , , JavaScript, . WebAssembly , JavaScript-. WebAssembly Node.js.

-


- (Rich Internet Applications, RIA) . , , ( ) JS-, .

, 20 . Sun Netscape Java- Netscape Navigator. JavaScript , , Java-. , Java-, — Java-. , . .

JavaScript , . RIA, , - Java -.

, RIA . Node.js , , , . JavaScript.

以下是一些示例:

  • Google Docs ( ), , .
  • , React, Angular Vue.js, , HTML, CSS JavaScript.
  • Electron — Node.js Chromium. - . , Visual Studio Code, Atom, GitKraken, Postman.
  • Electron/NW.js , -, , React, Angular, Vue, .

Java, , - -, JavaScript. , , - Sun Microsystems. Sun , . . Java- , Java Java Web Start. Java- Webstart-.

Java, , , IDE NetBeans Eclipse . Java , , Java.

JavaFX.

JavaFX, 10 , Sun iPhone. , Java, , , . Flash iOS. . JavaFX , , . - React, Vue.js .

JavaScript Node Java.

Java, - JavaONE. Java . , , , .


Java




总结


. «P-» (Perl, PHP, Python) Java, Node.js, Ruby, Haskell, Go, Rust, . .

, , , Java, Node.js, , , Node.js-. Java , Node.js . , , , Java, .

. , , Node.js - , - . . , XBRL-. XBRL Python, , , Python. , , , .

亲爱的读者们! , , JavaScript - , - Node.js, .

Source: https://habr.com/ru/post/zh-CN421741/


All Articles