2018年6月,ECMAScript 2015(
ES6 )标准庆祝成立三周年。 在ES6中,首先出现了许多新的JavaScript功能,其次,此标准开始了语言开发的新时代。 此外,这是JS的最后一次大规模发行,因为现在TC39实施了该标准的小规模年度发行计划,并且没有每隔几年再次发行。

在过去的四年中,ES6相当合理地引起了全世界的关注。 该材料的作者(我们今天将其翻译发表)说,一直以来,由于
Babel的缘故 ,他使用JS规范的现代版本编写了所有代码。 他认为已经过去了足够的时间来批判性地分析ES6的新功能。 特别是,他对自己使用过一段时间的软件很感兴趣,然后停止使用它,因为这会使他的代码恶化。
关于JS的弱点
道格拉斯·克罗克福德(Douglas Crockford)在他的
《 JavaScript:强项》
一书中也写了关于该语言弱点的内容。 他认为这是不值得使用的东西。 幸运的是,在ES6的创新中,没有像JS的一些老问题一样难看的东西,例如进行隐式类型转换的松散相等运算符,
eval()
函数和
with
语句。 ES6的新功能设计得更好。 但是,他有些事情我要避免。 我的JS“弱点”列表中的那些功能由于以下原因而在此列表中:
- 从本质上讲,它们是“陷阱”。 也就是说,它们似乎旨在执行某些操作,并且在大多数情况下,它们可以按预期工作。 但是,有时它们的行为异常,很容易导致错误。
- 他们增加了语言的数量,以换取一些小利益。 这样的机会给开发人员带来了一些小的好处,但是需要那些试图弄清楚他的代码的人具有某些机制的知识,这些机制通常隐藏在某个地方。 对于API功能,这是双重事实,当使用此功能意味着与某个开发人员编写的代码进行交互的其他代码必须知道此API功能的应用程序时。
现在,基于这些考虑因素,让我们谈谈ES6的弱点。
关键字const
在ES6之前,可以使用
var
关键字声明JavaScript中的变量。 此外,根本无法声明变量,因此即使将其用于函数中,它们也属于全局范围。 对象的属性可以充当变量的角色,并且可以使用
function
关键字声明
function
。
var
关键字具有某些功能。
因此,它允许您创建添加到全局对象或范围受函数限制的变量。 但是,
var
关键字不注意代码块。 另外,您可以在变量声明的命令之前的代码中引用使用
var
关键字声明的变量。 这种现象称为增加变量。 如果不考虑这些功能,可能会导致错误。 为了纠正这种情况,ES6引入了两个用于声明变量的新关键字:
let
和
const
。 他们解决了
var
的主要问题。 即,我们正在谈论使用这些关键字声明的变量具有块作用域的事实,例如,在循环中声明的变量在其外部不可见。 另外,使用
let
和
const
不允许在声明变量之前访问变量。 这将导致
ReferenceError
错误。 这是向前迈出的一大步。 但是,两个新关键字及其功能的出现导致了更多的混乱。
在声明之后,不能覆盖使用
const
关键字声明的变量(常量)的值。 这是
const
和
let
之间的唯一区别。 这个新机会看起来很有用,而且确实可以带来一些好处。 问题是
const
关键字本身。 使用它声明的常量的行为方式与大多数开发人员将“常量”概念相关联的方式不同。
const CONSTANT = 123; // "TypeError: invalid assignment to const `CONSTANT`" CONSTANT = 345; const CONSTANT_ARR = [] CONSTANT_ARR.push(1) // [1] - console.log(CONSTANT_ARR)
使用
const
关键字可防止将新值写入常量,但不会使此类常量引用的对象不可变。 当使用大多数数据类型时,此功能不能很好地防止更改值。 结果,由于使用
const
可能导致混淆,并且由于存在
let
关键字,因此
const
的存在显得多余,我决定始终使用
let
。
标记模板字符串
const
关键字是说明如何创建太多方法来解决太少问题的示例。 对于带标签的模板字符串,情况则相反。 TC39委员会认为此类字符串的语法是解决字符串插值和多行字符串的一种方法。 然后,他们决定通过使用宏扩展这种机会。
如果您以前从未看过标记过的模式字符串,请记住它们有点像字符串
装饰器 。 这是使用
MDN与他们合作的示例:
var person = 'Mike'; var age = 28; function myTag(strings, personExp, ageExp) { var str0 = strings[0]; // "that " var str1 = strings[1]; // " is a " // ( ) // , // , . // var str2 = strings[2]; var ageStr; if (ageExp > 99){ ageStr = 'centenarian'; } else { ageStr = 'youngster'; } return str0 + personExp + str1 + ageStr; } var output = myTag`that ${ person } is a ${ age }`; console.log(output); // that Mike is a youngster
标记的模板字符串不能被称为完全无用的。 这里是它们
的一些用途的
概述 。 例如,它们在清除HTML代码时很有用。 并且,目前,当您需要对任意字符串模板的所有输入数据执行相同的操作时,它们的应用程序将演示最准确的方法。 但是,这种情况相对较少,您可以使用适当的API进行相同的操作(尽管这样的解决方案更长)。 而且,要解决大多数问题,使用API不会比使用带标签的模板字符串更糟。 此功能不会向该语言添加新功能。 她增加了使用数据的新方法,那些必须阅读使用标记的模板字符串编写的代码的人应该熟悉的数据。 并且我努力确保我的代码保持尽可能整洁和易于理解。
重新设计的破坏性分配表达式
当用于解决简单任务时,该语言的某些功能看起来很棒,但是,当任务变得更加复杂时,这些功能可能会失控。 例如,我喜欢三元条件运算符:
let conferenceCost = isStudent ? 50 : 200
但是,借助它的帮助编写的代码,如果使用此运算符开始使用嵌套构造,将很难理解:
let conferenceCost = isStudent ? hasDiscountCode ? 25 : 50 : hasDiscountCode ? 100 : 200;
破坏性分配也可以这样说。 这种机制允许您从对象或数组中提取变量的值:
let {a} = {a: 2, b: 3}; let [b] = [4, 5]; console.log(a, b)
另外,使用它时,您可以重命名变量,获取嵌套值,设置默认值:
let {a: val1} = {a: 2, b: 3}; let [{b}] = [{a:3, b:4} , {c: 5, d: 6}]; let {c=6} = {a: 2, c: 5}; let {d=6} = {a: 2, c: 5}; console.log(val1, b, c, d)
所有这一切都很棒-直到涉及到使用所有这些功能来构建复杂的表达式为止。 例如,在下面的表达式中,声明了4个变量:
userName
,
eventType
,
eventDate
和
eventId
。 它们的值取自
eventRecord
对象结构中的不同位置。
let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let { user: { name: userName = "Unknown" }, event: eventType = "Unknown Event", metadata: [date: eventDate], id: eventId } = obj;
理解这样的代码几乎是不可能的。 如果您使用多个解构操作或完全放弃它们,则可以使用更具可读性的代码来解决此问题。
let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let userName = eventRecord.user.userName || 'Unknown'; let eventDate = eventRecord.metadata.date; let {event:eventType='UnknownEvent', id:eventId} = eventRecord;
我没有明确的指导方针,表明破坏性任务的表达方式需要重新设计。 但是,每当我看到一个类似的表达式并且无法立即理解它解决了什么问题,在其中使用了什么变量时,我都知道该是简化代码以提高其可读性的时候了。
默认导出
ES6具有一项不错的功能。 它在于开发人员如何借助以前常常在彼此竞争的各种库的帮助下实现以前所做工作的标准化。 因此,在规范中出现了类,承诺,模块。 这是JS开发人员社区在ES6之前使用的所有东西,可以在第三方库中找到它。 例如,ES6模块可以很好地替代溢出到AMD / CommonJS格式战争中的模块,并为组织导入提供方便的语法。
ES6模块支持两种主要的导出值方法:命名导出和默认导出,或默认导出:
const mainValue = 'This is the default export export default mainValue export const secondaryValue = 'This is a secondary value; export const secondaryValue2 = 'This is another secondary value;
一个模块可以使用多个命名的导出命令,但只能使用一个默认的导出命令。 导入使用默认导出命令导出的内容时,在导入文件中,您可以为默认导出的内容指定任何名称,因为在此操作过程中不会搜索任何名称。 使用命名导出时,尽管也可以重命名,但是您需要使用导出文件中的变量名。
// import renamedMainValue from './the-above-example'; // import {secondaryValue} from './the-above-example'; // import {secondaryValue as otherValue} from './the-above-example';
默认导出受到ES6标准
开发人员的特别关注 ,他们有意为其创建了一个更简单的语法。 但是,实际上,我发现使用命名导出技术更可取,原因如下。
- 使用命名导出时,默认情况下,导出变量的名称与导入变量的名称相对应,从而简化了对不使用智能开发工具的用户的搜索。
- 使用命名导出时,使用智能开发工具的程序员将获得诸如自动导入之类的便捷功能。
- 命名导出使您能够以合适的数量从模块中统一导出所需的任何内容。 默认导出将开发人员限制为仅导出单个值。 解决方法是,可以对具有多个属性的对象进行导出。 但是,这种方法失去了用于减少由webpack之类构建的JS应用程序大小的摇树算法的价值。 使用专门命名的模块可以简化工作。
通常,应注意的是,命名实体是一种好习惯,因为它使您可以在代码和有关此代码的对话中唯一地标识它们。 这就是为什么我使用命名出口。
总结
您刚刚了解了ES6的功能,根据本材料的作者,这是不成功的。 也许您加入了这一观点,也许没有。 任何编程语言都是一个复杂的系统,可以从不同的角度查看其功能。 但是,我们希望本文对所有试图编写清晰且高质量的代码的人有用。
亲爱的读者们! 您是否想避免现代JavaScript中的某些东西?
