您应该了解的 6 个高级 JavaScript 概念 1. 闭包用于扩展变量范围 2. 对象字面量用于传递可选参数 3. DOM 元素的上下文定位 4. 使用命名空间防止冲突 5. 混合应用程序开发 6. 渲染可读的 HTML

2025-05-24

你应该知道的 6 个高级 JavaScript 概念

1. 闭包扩展变量作用域

2. 对象字面量传递可选参数

3. DOM元素的上下文定位

4. 使用命名空间防止冲突

5.混合应用程序开发

6.渲染可读的HTML

1. 闭包扩展变量作用域

JavaScript 中的闭包是一个相当简单的概念,网上已经有很多深入的文章对其进行了讨论。然而,简单并不一定意味着简单,这一点从大量探讨该主题的文章中可以看出。

简而言之,闭包允许将变量的作用域扩展到函数的常见作用域限制之外。我喜欢 Jeremy Keith 在他的书《Bulletproof Ajax》中对闭包的描述:

“将关闭视为一种区域范围:比地方范围更广,但不如全球范围那么广。”

要创建闭包,你需要将一个函数嵌套在另一个函数中。内部函数可以访问其父函数作用域内的所有变量。这在面向对象的脚本中创建方法和属性时非常有用。以下是一个简单的示例,演示了闭包的用法:

function myObject() {
  this.property1 = "value1";
  this.property2 = "value2";
  var newValue = this.property1;
  this.performMethod = function() {
    myMethodValue = newValue;
    return myMethodValue;   
  };
  }
  var myObjectInstance = new myObject();  
  alert(myObjectInstance.performMethod());
Enter fullscreen mode Exit fullscreen mode

脚本的关键部分是嵌套的匿名函数(以​​绿色突出显示)以及 alert 函数中的方法调用(最后一行)。由于 alert 中的方法实际上是在调用一个嵌套函数,因此该方法能够读取名为 newValue 的变量的值,即使该变量不在匿名函数或方法的作用域内。

开发人员一直在使用闭包,尽管他们可能并不自觉,因为每当一个匿名函数嵌套在另一个函数中,并使用父函数作用域中的变量时,就会创建一个闭包。当该方法(内部函数)被调用时,闭包的威力就显现出来了,通常无法访问的值位于“局部”作用域内,因此可以像任何其他值一样使用。

请参阅以下参考资料,深入了解闭包及其与作用域的关系。我还强烈建议你阅读一本优秀的 JavaScript 高级书籍,其中对闭包相关概念进行了深入的讨论。

进一步阅读

  • 解释 JavaScript 作用域和闭包 (Robert Nyman)
  • JavaScript 中的闭包 (James Padolsey)
  • Jibbering.com 上的 JavasCript 闭包
  • JavaScript 闭包入门

2. 对象字面量传递可选参数

在处理可以接受大量可选参数的函数时,请记住以下实用的编码技巧。不必像传统方式那样传递大量参数,因为这可能会不必要地增加函数的复杂性,而是可以只传递一个参数,最终将其作为在对象字面量中声明的参数集合。

首先,让我们看看如何以典型的方式做到这一点,以便我们可以看到对比:

function showStatistics(name, team, position, average, homeruns, rbi) {
  document.write("<p><strong>Name:</strong> " + arguments[0] + "<br />");
  document.write("<strong>Team:</strong> " + arguments[1] + "<br />");

  if (typeof arguments[2] === "string") {
    document.write("<strong>Position:</strong> " + position + "<br />"); 
  }
  if (typeof arguments[3] === "number") {
    document.write("<strong>Batting Average:</strong> " + average + "<br />");
  }
  if (typeof arguments[4] === "number") {
    document.write("<strong>Home Runs:</strong> " + homeruns + "<br />");
  }
  if (typeof arguments[5] === "number") {
    document.write("<strong>Runs Batted In:</strong> " + rbi + "</p>"); 
  }
}
showStatistics("Mark Teixeira");
showStatistics("Mark Teixeira", "New York Yankees");
showStatistics("Mark Teixeira", "New York Yankees", "1st Base", .284, 32, 101);
Enter fullscreen mode Exit fullscreen mode

上述函数最多可以接受 6 个参数。前两个参数是必需的,因此在函数内部我们不检查它们是否存在。后 4 个参数不是必需的,因此只有当它们存在时我们才会显示它们的值。

我们调用该函数 3 次(最后 3 行),每次传入的参数数量都不同。您可以看到,如果传递的参数数量达到几十个甚至更多,代码看起来可能会有点混乱,并且更难维护或阅读。

现在让我们看看使用对象文字传递参数的相同代码:

function showStatistics(args) {
  document.write("<p><strong>Name:</strong> " + args.name + "<br />");
  document.write("<strong>Team:</strong> " + args.team + "<br />");
  if (typeof args.position === "string") {
    document.write("<strong>Position:</strong> " + args.position + "<br />"); 
  }
  if (typeof args.average === "number") {
    document.write("<strong>Average:</strong> " + args.average + "<br />");
  }
  if (typeof args.homeruns === "number") {
    document.write("<strong>Home Runs:</strong> " + args.homeruns + "<br />");
  }
  if (typeof args.rbi === "number") {
    document.write("<strong>Runs Batted In:</strong> " + args.rbi + "</p>");
  }
}

showStatistics({
  name: "Mark Teixeira"
});

showStatistics({
  name: "Mark Teixeira",
  team: "New York Yankees"
});

showStatistics({
  name: "Mark Teixeira",
  team: "New York Yankees",
  position: "1st Base",
  average: .284,
  homeruns: 32,
  rbi: 101
});
Enter fullscreen mode Exit fullscreen mode

从技术上讲,传递参数的第二种方法可能需要多一点代码,但如果参数数量较多,则会带来一些优势。

首先,函数本身被简化了,因为它只接受一个参数(args),该参数是从对象字面量传递过来的所有值的集合(名称、团队、位置等)。此外,由于值与参数引用之间的关联更加直接,实际的参数值也更易于阅读,并且易于理解、更新或修改。

如果函数只需要少量参数,则此方法没有必要,实际上可能会产生相反的效果。因此,请谨慎使用此技术,并且仅在您预见到参数集合会随着时间的推移难以维护的情况下使用。

进一步阅读

3. DOM元素的上下文定位

有时您需要遍历 DOM 并访问特定元素或元素组,但由于某些限制,您可能无法通过 HTML 代码中的 CSS 类名或 ID 直接访问这些元素。这可能是由于用户通过富文本编辑器生成的内容,或从数据库提取的动态内容。

无论如何,通过 JavaScript 访问这些未识别的 DOM 元素并非不可能。使用我称之为“上下文定位”的技术,您可以访问并修改 DOM 中的几乎任何元素。只要您拥有包含要定位元素的通用模板映射,您就可以访问并操作该元素,就像操作具有类名或 ID 的元素一样。

让我们创建一些基本的 HTML 代码作为示例页面:

<div id="header">
  <h1>Site Title</h1>
</div>
<div id="sidebar">
  <ul>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
  </ul>
</div>
<div id="content">
  <h2>Page Title</h2>
  <p><a href="#">Lorum Ipsum link here</a>. Pellentesque habitant morbi
     tristique senectus et netus et malesuada fames ac turpis egestas.
     Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, 
     ante. Donec eu libero sit amet quam egestas semper.
     Aenean ultricies mi vitae est. Mauris placerat eleifend leo.
     Pellentesque habitant morbi tristique senectus et netus et malesuada
     fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae,
     ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam
     egestas semper. Aenean ultricies mi vitae est. Mauris
     placerat eleifend leo.</p>
  <p><span style="color: red;">Pellentesque habitant morbi</span>
    tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum
    tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec
    eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
    Mauris placerat eleifend leo. Pellentesque habitant morbi tristique senectus
    et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam,
    feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit
    amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat
    eleifend leo.</p>    
</div>
<div id="footer">
   <p>Copyright | <a href="#">contact</a> | <a href="#">policy</a> | 
      <a href="#">privacy</a></p>
</div>
Enter fullscreen mode Exit fullscreen mode

使用上面的 HTML 代码,如果我们想要定位页面上的所有锚标签,我们可以收集它们并像这样操作它们:

var myLinkCollection = document.getElementsByTagName("a");

for (i=0;i<myLinkCollection.length;i++) {
  // do something with the anchor tags here
}
Enter fullscreen mode Exit fullscreen mode

但是,如果我们只想定位页脚中的锚标签,我们会根据它们的上下文或周围元素来定位它们,就像这样

var myFooterElement = document.getElementById("footer");
var myLinksInFooter = myFooterElement.getElementsByTagName("a");
for (i=0;i<myLinksInFooter.length;i++) {
  // do something with footer anchor tags here
}
Enter fullscreen mode Exit fullscreen mode

第一行获取了对页脚元素的引用。第二行收集了<a>页脚内的所有标签。然后我们循环遍历它们并执行所需的操作。因此,即使它们没有通过类名分组,也可以访问它们。

您可以使用节点属性完成相同的操作,如下所示。

var myLinkCollection = document.getElementsByTagName("a");

for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].parentNode.parentNode.id === "footer") {
    // do something with footer anchor tags here
  }
}
Enter fullscreen mode Exit fullscreen mode

类似的代码可用于定位“内容”部分内的单个锚标签。

我们还可以限制锚标签的搜索范围,使其仅包含设置了 href 属性的标签,以避免找到任何页内链接。我们使用 getAttribute 方法来实现这一点:

var myLinkCollection = document.getElementsByTagName("a");

for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].getAttribute("href")) {
    // do something with the anchor tags here
  }
}
Enter fullscreen mode Exit fullscreen mode

最后,您会注意到有一个带有内联样式的标签。该内联样式可能是通过内容管理系统生成的,因此您可能无法直接编辑它。您可以像这样定位所有带有内联样式的元素:

var myLinkCollection = document.getElementsByTagName("span");

for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].getAttribute("style")) {
    // do something with all anchors that have inline styles
  }
}
Enter fullscreen mode Exit fullscreen mode

上下文定位的可能性是无穷无尽的,如果您使用能够规范浏览器差异并简化 DOM 操作的 JavaScript 库,则可以提供更多选项。

进一步阅读:

4. 使用命名空间防止冲突

如果您正在进行大量原始 JavaScript 编码,并怀疑可能会对您正在处理的同一页面进行添加,则可以通过为代码提供自己的命名空间来防止将来与您的代码发生任何冲突。

面向对象的 JavaScript 实现了类似命名空间的原则,因为属性和方法都是在对象内部声明的,因此发生冲突的可能性较小。然而,对象名称可能会引发冲突。而且冲突很可能“悄无声息”地发生,因此您可能不会立即收到警报。

通过创建唯一的命名空间,可以避免所有冲突。让我们使用 showStatistics 函数来演示如何将代码封装到它自己的命名空间中:

if (typeof MY == "undefined") {
  MY = new Object();
  MY.CUSTOM = new Object();
}

MY.CUSTOM.namespace = function() {
  function showStatistics(args) {
    document.write("<p><strong>Name:</strong> " + args.name + "<br />");
    document.write("<strong>Team:</strong> " + args.team + "<br />");
    if (typeof args.position === "string") {
      document.write("<strong>Position:</strong> " + args.position + "<br />");
    }
    if (typeof args.average === "number") {
      document.write("<strong>Average:</strong> " + args.average + "<br />");
    }
    if (typeof args.homeruns === "number") {
      document.write("<strong>Home Runs:</strong> " + args.homeruns + "<br />");
    }
    if (typeof args.rbi === "number") {
      document.write("<strong>Runs Batted In:</strong> " + args.rbi + "</p>");
    }
  }

  showStatistics({
    name: "Mark Teixeira",
    team: "New York Yankees",
    position: "1st Base",
    average: .284,
    homeruns: 32,
    rbi: 101
  });
}
MY.CUSTOM.namespace();
Enter fullscreen mode Exit fullscreen mode

前几行代码通过检查“ MY ”对象是否已存在来创建命名空间。这个对象可以是任何你想要的名称。只需选择一个你认为不会再用到的名称即可。创建MY对象后,我们就可以创建“ CUSTOM ”对象作为MY对象的属性。然后,我们的命名空间函数就变成了MY.CUSTOM对象的方法。请记住,“ MY ”、“ CUSTOM ”和“ namespace ”都可以是你自己自定义的名称。我选择这些是为了演示。如果你愿意,它们也可以是CHEESEBURGER.ONIONS.pickles !

showStatistics函数与之前使用对象字面量传入值的示例完全相同。但在本例中,整个函数(包括对象字面量)都封装在my.custom.namespace中。最后一行使用点符号调用整个函数,该函数的运行方式与正常情况下完全相同,只是它被保护起来,避免与另一个名为“ showStatistics ”的函数发生冲突。

进一步阅读:

5.混合应用程序开发

如果结合使用 JavaScript 库和原始 JavaScript 代码,您可以创建功能强大的 JavaScript 应用程序。许多 JavaScript 库用于实现“精美”的动画和其他可自定义的效果(有时通过插件实现),这些效果通常不需要添加太多内容,只需添加一些自定义值即可。

另一方面,有时你可能想要完成客户端的特定请求。这可能是库中没有的功能,需要大量的代码编写,甚至可能要用到 Ajax 和各种 DOM 方法。

重新发明轮子毫无意义。您可以实现自己喜欢的 JavaScript 库,并利用其简化的 Ajax 调用、DOM 方法以及对浏览器差异的规范化。这样,您就可以充分利用库的优势,同时仍然可以创建特定于项目的自定义脚本。

进一步阅读:

6.渲染可读的HTML

最后,这是一种在需要通过 JavaScript 动态生成数十行 HTML 代码的情况下使用的技术。请看以下示例:

var pageContainer = document.getElementById("container");
var pageTitle = "Content Title";
var authorBio = "Mr. Lorum Ipsum";
var pageContent = "Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here.";
var footerContent = "Copyright 2009";
var HTMLCode = '\n<h1>' + pageTitle + '</h1>\n
               <div id="content">\n
               <p>' + pageContent + '</p>\n
               <div id="author_bio">\n
               <p>' + authorBio +'</p>\n
               </div>\n
               </div>\n
               <div id="footer">
               <p>' + footerContent + '</p>\n
               </div>\n';

pageContainer.innerHTML = HTMLCode;
Enter fullscreen mode Exit fullscreen mode

上面需要注意的是声明 HTMLCode 变量值的那行。由于使用了“换行符”,它在生成的源代码中渲染时看起来就像是完美的 HTML 代码。但如果这行代码再长一点,在 .js 文件中阅读和维护起来就会变得非常困难。

下面是与上面相同的代码,但实现了一种更有条理的显示 HTML 的方法:

var pageContainer = document.getElementById("container");
var pageTitle = "Content Title";
var authorBio = "Mr. Lorum Ipsum";
var pageContent = "Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here.";
var HTMLCode =  '\n' +
                '<h1>' + pageTitle + '</h1>\n'
                '<div id="content">\n' +
                  '<p>' + pageContent + '</p>\n' +
                  '<div id="author_bio">\n' +
                    '<p>' + authorBio + '</p>\n' +
                  '</div>\n'
                '</div>\n' +
                '<div id="footer">' +
                  '<p>' + footerContent + '</p>\n' +
                '</div>\n';

pageContainer.innerHTML = HTMLCode;
Enter fullscreen mode Exit fullscreen mode

现在代码可读性更强,并且符合实际 HTML 页面的渲染方式。它甚至包含正确的 HTML 缩进,并且仍然使用换行符来正确格式化输出的 HTML。

结论

虽然我没有对本系列中涉及的每个概念进行详细的解释,但我希望这份列表能够为初级和中级程序员提供一些相当高级的 JavaScript 策略的概述,他们可以在未来的项目或实验中实施这些策略。

请随意评论我提到的任何高级 JavaScript 概念以及您在自己的应用程序中使用它们的一些具体方法。

相关内容

文章来源:https://dev.to/codehunterbd/6-advanced-javascript-concepts-you-should-know-4lfe
PREV
追踪你的进步来提高你的信心
NEXT
我如何学习 Go 编程