JavaScript编程语言是当今Web开发人员必不可少的工具。网站向浏览器提供了越来越多的JavaScript,以提高交互性。客户端JavaScript越复杂,用户体验可能就越容易出错和脆弱。为什么我们需要谈论健壮的JavaScript,我们如何实现它?
在前端Web技术的三位一体-HTML、CSS和JavaScript-中,后者与其他技术不同。HTML和CSS分别是用于构造文本文档和表达样式规则的特殊目的的声明性语言。HTML和CSS的设计方式都允许浏览器以容错的方式处理代码。这些设计特征是允许向后和向前兼容所必需的。
然而,JavaScript是一种完全成熟的编程语言,恰好在网页上下文中运行。JavaScript只有几个内置的故障保护和兼容机制。JavaScript的能力是无限的,而HTML和CSS的能力最小,无法满足其特殊用途。
HTML、CSS和JavaScript可能失败的方式有数千种,而且几乎在每个网站上每天都会发生这种情况。但当HTML或CSS失败时,其影响相当有限。尽管有几个HTML和CSS错误,网页仍然可以使用。相反,单个JavaScript错误可能会导致整个网站无法使用。有时有恢复的方法,但用户可能不知道。
在本指南中,我们将调查JavaScript可能失败的原因,以及如何以确保网站正常工作的优雅方式防止或处理这些错误。
为Web编写客户端JavaScript与为其他平台编程不同。没有一个开发人员可以依赖的定义良好的运行时环境。没有一种硬件架构或设备类型。没有一家供应商定义和构建运行时、编译器和工具。
Web是一个开放的、独立于供应商的、异构的发布平台。它是由几个不同质量的技术标准结合在一起的。新标准频繁出现,旧标准被修订或废止。不同的标准化组织遵循不同的程序规则。
有一些技术行为是标准化的,也是主要浏览器一致同意的。例如,基本的HTML元素得到了很好的支持。
有一些技术行为是标准化的,主要浏览器对此并不认同。例如,浏览器在其实现中可能有错误,或者只是还不支持更新的标准。
有一些技术行为没有标准化,主要浏览器都同意。标准可能会省略一些细节,让实现者来决定。尽管如此,浏览器供应商为了保持一致性,照搬了其他浏览器的详细行为。
有一些技术行为没有标准化,主要浏览器也不同意。通常,在进入标准化过程之前,新的网络技术是作为专有实验诞生的。有些技术从未被广泛采用,被遗忘了。
在具有不同硬件能力、互联网连接等的设备上的不同操作系统上运行的多个版本的多个相关浏览器。网络客户端不在它们的控制之下的事实使来自其他域的开发者发疯。他们认为网络是最具敌意的软件运行时环境。他们明白网络客户端的多样性是一个弱点。
Web的支持者反驳说,这种异质性和不一致性实际上是Web的优势所在。网络是开放的,它无处不在,它的访问门槛很低。网络是自适应的,并且不断吸收新的技术和应用领域。到目前为止,还没有其他软件环境表现出这种程度的灵活性。
前端开发人员受益于一个不断发展和创新的网络。尤其是JavaScript开发人员,一旦某些浏览器指定并实现了新的语言功能,他们可能很快就会采用这些功能。在本指南中,我们将探讨如何以向后兼容的方式使用新功能。
客户端JavaScript编程确实是一个雷区。但有一个简单的苏格拉底原则将照亮我们的道路:不要认为任何事情都是理所当然的。不要指望任何东西。质疑你的信仰。如果您知道您对运行JavaScript代码的客户端一无所知,那么您可以将毫无根据的假设转化为合理的知识。
假设在JavaScript中是必要和不可避免的,但我们需要拥有这些假设。每个JavaScript程序都对其运行时环境做了很多假设。虽然有一个较低的进入门槛当然是可取的,但该计划需要完成一项任务。需求应该与所提供的功能保持良好的平衡关系。
没有单一的技术规范来定义JavaScript,而是一大堆规范。
ECMAScript规范定义了语言的核心:基本语言特性、语法、执行和标准库。每年都会发布新版本的ECMAScript。在撰写本文时,ECMAScript 2017,Edition 8(也称为ECMAScript 8)是最新版本。
单独使用ECMAScript时,您无法执行任何有用的操作。例如,无法读取或输出任何数据。ECMAScript没有定义在其中执行程序的所谓主机环境。它允许多种可能的主机环境。浏览器中的HTML文档是一种可能的宿主环境。js是另一个流行的工具。
我们这里感兴趣的主机环境主要在HTML规范中定义。它不仅将HTML定义为标记语言,还定义了JavaScript在HTML文档上下文中的执行方式。它定义了JavaScript如何访问和更改文档。为此,它依赖于另一个规范:文档对象模型(DOM)。
HTML和DOM规范定义了客户端JavaScript正在处理的主要对象:节点、元素和事件。基本对象包括Window、window.alert()、Document、Document.body、Document.getElementById()和Document.createElement()。
还有很多其他规范将更多API添加到浏览器的JavaScript环境中。网络平台:浏览器技术给出了一个概述。
健壮性是什么意思?在日常语言中,如果一件东西是由坚固、坚固的材料制成的,并且不知何故能抵抗外力,那么它就被认为是坚固的。你可以长时间使用它,你可以不小心掉下来,你甚至可以用锤子敲打它,或者把它扔来扔去,但它不会断。
根据这个定义,一块硬质金属可以是坚固的,但也可以是有弹性的弹力球。科学寻找将强度与一定的抗力能力相结合的材料,如延展性和弹性。
结构也可以是健壮的。想一想使用某种类型桁架的格构塔。它又大又结实,但又轻而模块化。
同样,在计算机科学中,健壮的程序不仅在普通条件下表现良好,而且在强调其设计者假设的不寻常条件下也表现良好。(Linux信息项目)。当出现错误时,程序不会停止执行。当输入数据或用户输入无效或伪造时,它不会失败。
所以稳健性就是做出明智的假设。当开发人员的假设没有得到满足时会发生什么呢?让我们来看看健壮性的几个概念。
在Web开发的上下文中,优雅的降级意味着构建一个功能齐全的网站,然后为缺乏某些功能的客户端添加备用功能。
网站一开始有一大套固定的功能,因此也有一大套要求。客户端可能不符合要求,因此依赖于它的功能可能不可用。如果没有满足要求,站点不会中断,但会优雅地处理这种情况。例如,它会后退到更简单的版本。
对于JavaScript,优雅的降级可能意味着使用最新的JavaScript语言功能和API实现站点。但每一次使用都必须由功能检查来保护。如果浏览器没有所需的功能,则会激活一个更简单的实现。
网站仍然有一组所需的功能和一组客户要求。但是实现从最小的功能集开始。该网站的第一个版本的进入门槛较低,因为它只使用成熟的技术。
然后,构建第二个版本,通过添加新功能来增强第一个版本。增强版检查客户端是否支持某些Web技术,然后安全地使用它们。如果客户端满足某项功能的要求,则会激活该功能。
重复此过程,创建第三、第四、第五个版本,依此类推。这就是为什么它被称为渐进式增强。从理论上讲,网站可以无休止地增强,同时保持健壮,功能有限的设备和浏览器可以访问。
优雅降级和渐进式增强是同一概念的两个实现,但有不同的转折。
优雅的降级旨在利用尖端技术-登月技术获得完整的体验。建立一个完美的网站需要大量的时间和资源。当构建这样的页面时,它通常只能在最新设备上的一个浏览器中工作。
然后,需要在事实之后添加复杂的后备措施。事实证明,这是困难和乏味的。对于某些新的浏览器功能,开发等效的后备几乎是不可能的。但更重要的是,增加后备力量往往被忽视。当预算几乎耗尽时,Web开发人员倾向于添加“此站点需要浏览器X”的标志,而不是编写适当的后备方案,而不是排除许多用户。
相反,渐进式增强遵循“最小可行产品”的产品开发学派。我们的目标是快速发布一个可以正常工作的网站。这第一个版本不是最友好的,当然也不是它的竞争对手中最耀眼的。但该网站在每一台设备上都能顺利运行。无论它做什么,它都是可靠的。
现在可以安全地添加和快速部署利用最新浏览器功能的增强功能。在开发过程中,网站没有一个阶段只为一小部分用户服务。
人们普遍认为渐进式增强提供了更多的好处,但当应用到单个网站时,这两种方法都应该考虑,甚至应该混合使用。
如果你正在计划一个在核心体验中依赖尖端技术的“登月计划”,比如视频会议或增强现实,优雅的降级可能会帮助你建立一个更具包容性的网站。
如果您正在规划一项具有坚如磐石的基础和要求苛刻的附加服务(如实时数据分析和可视化)的服务,渐进式增强可能会帮助您在不丢失可访问性的情况下构建高性能。
当应用于JavaScript编程时,优雅降级和渐进式增强都提出了许多实际问题。如何应用后备?它与其余代码的集成情况如何?在多大程度上可以在现有版本的基础上进行构建和增强?难道有时候不需要划清界限吗?您需要找到特定于您的项目的答案。
优雅降级和渐进式增强都依赖于检查客户端的功能。我们稍后要讨论的关键技术称为特征检测。
健壮性的另一个概念是容错。技术系统被认为是容错的,即即使子系统发生故障,整个系统也能继续运行。
系统由关键子系统和非关键子系统组成。关键子系统提供基础设施并协调其他部分。如果它们失败了,通常整个系统都会失败。相比之下,非关键子系统可以从错误中恢复。或者他们以受控的方式关闭并报告关闭,以允许备份系统接管。
尽管优雅降级和渐进式增强是Web技术的固有原则,但容错不是。这可能是实现健壮性的最困难但最有益的技术。
特别是,容错很难在JavaScript中实现。不小心使用JavaScript与容错相反。通常,如果一个操作失败,如果发生一个异常,整个调用堆栈或整个程序都会崩溃。
在JavaScript中实现容错意味着将代码划分为独立的沙盒子系统。它们中只有几个是关键的。它们中的大多数应该是非关键的。如果后者因错误而失败,则需要捕获并处理错误。其他子系统和整个系统不应受到影响。
javascript还不支持原生沙箱的定义,但是我们可以使用像Try…这样的现有技术。抓住并承诺达到预期的效果。
乔恩·波斯特尔(Jon Postel)是一位帮助设计互联网核心技术的计算机科学家。他编辑了基本互联网协议的技术规范,称为请求注解(RFC)。
在1980年1月发布的RFC 790中,Postel首次描述了Internet协议(IPv4)。对于实现应该如何运行,有一个精确的描述:
协议的实现必须是健壮的。每个实现都必须期望与由不同个人创建的其他实现进行互操作。虽然本规范的目标是明确说明协议,但也存在不同解释的可能性。一般来说,实现在发送行为上应该保守,在接收行为上应该自由。也就是说,它应该小心发送格式良好的数据报,但应该接受它可以解释的任何数据报(例如,不反对含义仍然清晰的技术错误)。
在1980年1月发布的RFC 761中,Postel描述了美国国防部使用的传输控制协议(TCP):
TCP实现应该遵循健壮性的一般原则:在所做的事情上要保守,在接受他人的东西时要自由。
今天,这一原理通常被称为波斯特尔定律。虽然最初的上下文是非常特定的 - 处理广域计算机网络上的数据包的 -,但今天它被应用于读取、解析和处理用户输入、文件格式或其他结构化数据的所有程序。
例如,自由的、容错的HTML5解析器定义和保守的HTML5语法定义是Postel定律的应用。
就我个人而言,我认为波斯特尔定律不应被视为稳健性的一般原则。我同意某些观点,即程序应该接受它可以解释的数据(例如,不反对含义仍然清晰的技术错误)。但这条规则需要仔细解读。
在本指南中,我并不是说每个程序都应该在它接受的内容上是自由的。我发现更重要的是,每个程序都要明确地说明它接受什么,对技术错误直言不讳,并且有定义良好的错误处理。
将JavaScript添加到网站假设客户端下载并执行代码。对于许多自动化的Web客户端来说,情况并非如此。大多数机器人和网络爬虫都会说HTTP、HTML,可能还有一些CSS,但通常不会说JavaScript。
有些人试图在JavaScript代码中搜寻URL或其他有价值的信息。有些人试图分析JavaScript以发现恶意软件或安全漏洞。有些人甚至试图在假浏览器环境中执行JavaScript。
这些机器人的共同点是它们本身对JavaScript不感兴趣。JavaScript通常使网页具有交互性,但机器人的目标是在不模拟用户交互的情况下分析页面。
例如,搜索引擎需要评估页面对于查询是否有价值。因此,搜索引擎爬虫对文本内容、语义标记、超链接以及可能的媒体文件感兴趣。
这样的爬虫程序需要简单的代码,以便能够快速解析以找到有价值的数据。比如HTML代码。执行任意JavaScript既复杂、速度慢,又存在潜在的安全风险。一些爬虫可能无论如何都会这样做,但只是作为查找文本内容、语义标记、超链接等的一种方式。
如果一个网站关心一个像样的搜索引擎排名,它应该让爬虫很容易找到有意义的、独特的、结构化的文本内容。HTML是呈现此类内容的最佳技术。这意味着无需JavaScript即可访问相关内容,只需查看服务器返回的HTML即可。所有内容都应该可以通过普通超链接访问,如<;a href=";…。";>;…。<;/a>;。
对于机器人无法读取或不应该读取的复杂交互性和内容,可以使用JavaScript。
虽然机器人避免运行JavaScript,但人类通常使用运行JavaScript的浏览器。现在几乎所有的浏览器都有运行JavaScript的能力。但是用户或其管理员可能只允许某些来源的JavaScript,或者可能已经完全禁用了JavaScript执行。
禁止执行任意JavaScript有很好的安全原因。由于JavaScript是一种成熟的编程语言,因此处理它比Web上的任何其他格式都更复杂、更容易出错。浏览器向JavaScript代码公开了几个关键API。因此,JavaScript是浏览器攻击最频繁的攻击载体。
JavaScript还被用来侵犯用户的隐私。特别是,广告业收集和组合跨不同站点使用JavaScript获得的信息。JavaScriptAPI允许读取机器硬件和软件的详细信息,并将数据保存在机器上。这些特征被滥用来创造独特的“指纹”和广泛的用户档案:访问过的网站、搜索词、购买历史、兴趣;还有年龄、性别、地点、婚姻状况、职业、收入、种族、政治观点等。
为了保护用户,广告和隐私拦截器以及公司Web代理可能会忽略来自某些主机的JavaScript或限制对某些JavaScript API的访问。一些安全代理甚至会更改作者的JavaScript代码。
JavaScript作者需要了解拦截器和Web代理是如何工作的。它们通常将URL与允许列表或阻止列表匹配。确保提供JavaScript服务的主机(example.org)不在阻止列表中。在具有允许列表的公司内部网中,确保主机在此列表中。还要避免URL路径中可能触发阻止的可疑模式,如ad.js。
由于广告和侵犯隐私的脚本通常是从第三方服务器加载的,因此拦截器倾向于允许来自相同域的JavaScript,并可能阻止来自不同域的JavaScript。确保您的脚本位于相同的域、资产的自定义域或知名的可信内容交付网络中(请参阅下一章)。
在移动网络接入的时代,不稳定的互联网连接是常态。从客户端到服务器的连接经常中断。有时浏览器会重新建立连接以再次发送请求。有时,用户需要手动重新加载页面,以便完全加载所有部分。
网络中断对JavaScript的影响比Web上的其他格式更负面。HTML、CSS、图像和视频可以增量加载和处理。如果传输了一半的HTML代码并且连接断开,浏览器仍可以呈现半个页面。像JPEG和PNG这样的图像格式有。
..