JavaScript指南第6部分:异常,分号,模板文字

JavaScript教程翻译的这一部分的主题将是异常处理,自动分号功能和模板文字。

第1部分:第一个程序,语言功能,标准
第2部分:代码样式和程序结构
第3部分:变量,数据类型,表达式,对象
第4部分:功能
第5部分:数组和循环
第6部分:异常,分号,通配符文字
第7部分:严格模式,此关键字,事件,模块,数学计算
第8部分:ES6功能概述
第9部分:ES7,ES8和ES9标准概述



异常处理


在代码执行过程中发生问题时,将其表示为JavaScript中的异常。 如果不采取措施来处理异常,则在异常发生时,程序将停止,并在控制台中显示错误消息。

考虑以下代码片段。

let obj = {value: 'message text'} let notObj let fn = (a) => a.value console.log(fn(obj)) //message text console.log('Before') //Before console.log(fn(notObj)) //,    console.log('After') 

在这里,我们有一个计划用于处理具有value属性的对象的value 。 她归还了此财产。 如果将此功能用于其预期目的,即,将其设计为可以使用的对象传送给该函数,则在执行该函数时不会产生任何错误。 但是,如果您传递了不适当的内容(在我们的情况下为已声明但未初始化的变量),则当您尝试访问undefined值的value属性时会发生错误。 控制台中将显示一条错误消息,程序执行将停止。

这是在Node.js中运行此代码时的样子。


Node.js中的TypeError异常

如果网页的JS代码中发生类似情况,类似的消息将发送到浏览器控制台。 如果这是在真实程序中发生的,例如-在Web服务器代码中,则此行为是非常不希望的。 最好有一种机制,允许在不停止程序的情况下捕获错误,然后采取措施纠正错误。 这种机制存在于JavaScript中;它由try...catch构造表示。

try施工尝试〜抓


try...catch构造允许您捕获和处理异常。 即,它包括一个try块和一个catch ,其中try块包括可能导致错误的代码, catch在发生错误时转移控制。 try块并不完全包含所有程序代码。 那些可能导致运行时错误的部分放在此处。 例如,调用必须与从外部源接收到的某些数据一起使用的函数。 如果此类数据的结构与功能预期的结构不同,则可能会发生错误。 这是try...catch设计图的样子。

 try { // ,     } catch (e) { //  } 

如果代码执行没有错误,则不执行catch (异常处理程序)。 如果发生错误,则将错误对象转移到那里,并采取一些措施来解决此错误。

我们在示例中应用此构造,并通过其帮助来保护程序的危险部分-调用函数fn()那些部分。

 let obj = {value: 'message text'} let notObj let fn = (a) => a.value try {   console.log(fn(obj)) } catch (e) {   console.log(e.message) } console.log('Before') //Before try {   console.log(fn(notObj)) } catch (e) {   console.log(e.message) //Cannot read property 'value' of undefined } console.log('After') //After 

让我们看看在Node.js中执行此代码的结果。


Node.js中的错误处理

如您所见,如果将本示例与上一个示例进行比较,现在将执行所有代码,一个位于问题行之前的代码,以及一个位于问题行之后的代码。 我们只需将错误对象的message属性的值打印到控制台即可“处理” 错误 。 实际使用的代码中发生的错误将如何处理取决于该错误。

我们在上面讨论了try...catch块,但实际上,此构造包括另一个块finally

▍终于挡


finally块包含无论try块中运行的代码是否发生错误都可以运行的代码。 这是它的外观。

 try { //  } catch (e) { //  } finally { //  } 

如果try...catch...finally块没有try...catch...finally块,则也可以使用try...catch...finally catch 。 在这种方法中,它的使用方式与使用catch的构造相同,例如,释放try块中占用的资源。

▍嵌套尝试块


尝试块可以嵌套在一起。 在这种情况下,将在最近的catch处理异常。

 try { //  try {   //   } finally {   // -  } } catch (e) { } 

在这种情况下,如果内部try块中发生异常,则将在外部catch对其进行处理。

▍自生异常


您可以使用throw语句自行抛出异常。 这是它的外观。

 throw value 

执行该指令后,控制权转移到最近的catch ,或者如果找不到该块,则程序停止。 异常值可以是任何值。 例如,用户定义的错误对象。

关于分号


在JavaScript中使用分号是可选的。 一些程序员没有它们,而是依靠自动排列系统,仅将它们放置在绝对必要的地方。 有些人喜欢将它们放置在任何可能的地方。 本材料的作者指的是不使用分号的程序员。 他说,他决定在2017年秋季不设置它们,方法是将Prettier设置为在没有显式插入的情况下可以删除它们。 在他看来,没有分号的代码看起来更自然,更易于阅读。

也许可以说,就分号而言,JS开发人员社区分为两个阵营。 同时,有两种JavaScript样式指南规定了明确的分号放置,并建议不使用这些样式指南。

由于JavaScript具有用于自动分号的系统(自动分号插入,ASI),因此所有这些都是可能的。 但是,在JS代码中,在许多情况下都可以不用这些字符,并且在准备执行代码时自动放置分号这一事实并不意味着程序员不需要了解发生这种情况的规则。 这些规则的无知会导致错误。

automatic自动分号规则


在以下情况下,JavaScript解析器在解析程序文本时会自动添加分号:

  1. 当下一行以中断当前代码的代码开头时(某个命令的代码可能位于多行中)。
  2. 当下一行以字符}开头时,它将关闭当前块。
  3. 当检测到带有程序代码的文件末尾时。
  4. return命令一致。
  5. break命令一致。
  6. throw命令一致。
  7. continue命令一致。

▍无法正常运行的代码示例


以下是一些说明上述规则的示例。 例如,您认为以下代码片段的执行结果将显示什么?

 const hey = 'hey' const you = 'hey' const heyYou = hey + ' ' + you ['h', 'e', 'y'].forEach((letter) => console.log(letter)) 

尝试执行此代码时,将Uncaught TypeError: Cannot read property 'forEach' of undefined错误Uncaught TypeError: Cannot read property 'forEach' of undefined系统的Uncaught TypeError: Cannot read property 'forEach' of undefined基于规则1,它尝试按以下方式解释代码。

 const hey = 'hey'; const you = 'hey'; const heyYou = hey + ' ' + you['h', 'e', 'y'].forEach((letter) => console.log(letter)) 

该问题可以通过在第一个示例的倒数第二行之后加上分号来解决。

这是另一段代码。

 (1 + 2).toString() 

执行结果将是字符串"3"的输出。 但是,如果下一个代码片段中出现类似的内容,会发生什么呢?

 const a = 1 const b = 2 const c = a + b (a + b).toString() 

在这种情况下,将出现TypeError: b is not a function错误TypeError: b is not a function因为上述代码将按以下方式解释。

 const a = 1 const b = 2 const c = a + b(a + b).toString() 

现在让我们看一个基于规则4的示例。

 (() => { return {   color: 'white' } })() 

您可能会认为此IIFE将返回包含color属性的对象,但实际上并非如此。 而是,该函数将返回undefined因为系统在return命令之后添加了分号。

为了解决类似的问题,必须将对象文字的大括号放在与return命令相同的行上。

 (() => { return {   color: 'white' } })() 

如果查看下面的代码片段,您可能会认为它将在消息框中显示0

 1 + 1 -1 + 1 === 0 ? alert(0) : alert(2) 

但它输出2,因为根据规则1,此代码表示如下。

 1 + 1 -1 + 1 === 0 ? alert(0) : alert(2) 

在JavaScript中使用分号时应格外小心。 您可以遇到分号的热心支持者及其对手。 实际上,在确定代码中是否需要分号时,您可以依靠JS支持它们的自动替换这一事实,但是每个人都必须自己决定自己的代码中是否需要分号。 最主要的是一致且合理地应用所选方法。 关于分号的放置和代码的结构,我们可以建议以下规则:

  • 使用return命令,在命令的同一行上安排应从函数返回的内容。 breakthrowcontinue命令也是如此。
  • 请特别注意新代码行以方括号开头的情况,因为该行可以自动与上一行合并,并由系统呈现为尝试调用函数或尝试访问数组元素的情况。

通常,可以说,无论您是自己放置分号,还是依靠分号的自动放置,都对代码进行测试,以确保其完全按预期工作。

引号和通配符


让我们谈谈在JavaScript中使用引号的功能。 即,我们正在谈论JS程序中允许的以下引号类型:

  • 单引号。
  • 双引号。
  • 反引号。

通常,单引号和双引号可以认为是相同的。

 const test = 'test' const bike = "bike" 

它们之间几乎没有区别。 也许唯一明显的区别是,在用单引号引起来的字符串中,您需要对单引号的字符进行转义,而在用双引号引起来的字符串中,字符是双精度。

 const test = 'test' const test = 'te\'st' const test = 'te"st' const test = "te\"st" const test = "te'st" 

在不同的样式指南中,您可以找到使用单引号的建议和使用双引号的建议。 该材料的作者说,他努力在JS代码中仅使用单引号,而仅在HTML代码中使用双引号。

Backticks在2015年发布的ES6标准中出现在JavaScript中。 除了其他新功能外,它们还可以方便地描述多行字符串。 也可以使用常规引号指定此类字符串-使用转义序列\n 。 看起来像这样。

 const multilineString = 'A string\non multiple lines' 

倒逗号(通常用于输入它们的按钮位于键盘上数字键1的左侧)不带\n

 const multilineString = `A string on multiple lines` 

但是反引号的可能性不限于此。 因此,如果使用反引号描述字符串,则可以使用构造${}将来自JS表达式计算的值替换为字符串。

 const multilineString = `A string on ${1+1} lines` 

这样的字符串称为模板文字。

模板文字具有以下功能:

  • 它们支持多行文本。
  • 它们使插值字符串成为可能;可以在其中使用内置表达式。
  • 它们使您可以使用带标签的模板,从而可以创建自己的特定于域的语言(DSL,特定于域的语言)。

让我们谈谈这些功能。

line多行文字


设置带反引号的多行文本时,您需要记住,此类文本中的空格与其他字符一样重要。 例如,考虑以下多行文字。

 const string = `First               Second` 

他的结论大致如下。

 First               Second 

就是说,事实证明,当在编辑器中输入此文本时,程序员可能希望输出时的FirstSecond严格地在彼此之下出现,但实际上并非如此。 为了解决此问题,您可以使用换行符开始多行文本,并在关闭反引号之后立即调用trim()方法,该方法将删除行首或结尾的空格。 此类字符尤其包括空格和制表符。 行尾字符也将被删除。

看起来像这样。

 const string = ` First Second`.trim() 

▍插值


这里的插值是指将变量和表达式转换为字符串。 这是使用${}构造完成的。

 const variable = 'test' const string = `something ${ variable }` //something test 

您可以在${}块中添加任何内容,甚至可以是表达式。

 const string = `something ${1 + 2 + 3}` const string2 = `something ${foo() ? 'x' : 'y' }` 

文本something 6将进入string常量,或者文本something x或文本something y将被写入常量string2 。 这取决于函数foo()返回true还是false(此处使用三元运算符,如果在问号之前为true,则返回三进制运算符,否则返回在问号之后的内容。冒号之后)。

▍带标签的模板


带标记的模板在许多流行的库中使用。 其中包括样式化组件ApolloGraphQL

这种模式输出的内容取决于该函数定义的某些逻辑。 这是我们的出版物之一中的一个稍作修改的示例,说明了如何使用带标签的模板字符串。

 const esth = 8 function helper(strs, ...keys) { const str1 = strs[0] //ES const str2 = strs[1] //is let additionalPart = '' if (keys[0] == 8) { //8   additionalPart = 'awesome' } else {   additionalPart = 'good' } return `${str1}${keys[0]}${str2}${additionalPart}.` } const es = helper`ES ${esth} is ` console.log(es) //ES 8 is awesome. 

在这里,如果数字8esth常数写的,那么ES 8 is awesome行将是es 。 否则,将有另一行。 例如,如果esth数字6 ,则看起来ES 6 is good

样式化组件使用标记的模板来定义CSS字符串。

 const Button = styled.button` font-size: 1.5em; background-color: black; color: white; `; 

在Apollo,它们用于定义GraphQL查询。

 const query = gql` query {   ... } ` 

了解了标记模板的工作原理后,可以很容易地理解前面示例中的styled.buttongql只是函数。

 function gql(literals, ...expressions) { } 

例如, gql()函数返回的字符串可能是任何计算的结果。 此函数的literals参数是一个数组,其中包含模板文字的内容,该内容分为多个部分, expresions包含计算表达式的结果。

让我们分析下一行。

 const string = helper`something ${1 + 2 + 3} ` 

helper函数获取包含两个元素的literals数组。 在第一个文本后面有一个空格,在第二个文本中有一个空行-即表达式${1 + 2 + 3}与该行的末尾之间是什么。 espressions数组中将只有一个元素6
这是一个更复杂的示例。

 const string = helper`something another ${'x'} new line ${1 + 2 + 3} test` 

在这里,在helper函数中,下一个数组将作为第一个参数。

 [ 'something\nanother ', '\nnew line ', '\ntest' ] 

第二个数组将如下所示。

 [ 'x', 6 ] 

总结


今天,我们讨论了异常处理,分号自动替换以及JavaScript中的模板文字。 下次,我们将介绍该语言的一些更重要的概念。 特别是-在严格模式下工作,计时器,数学计算。

亲爱的读者们! 您是否在JavaScript中使用标记模板的功能?

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


All Articles