什么是 JavaScript?

欢迎来到 MDN 初学者 JavaScript 课程!在本文中,我们将从高层次来看待 JavaScript,回答诸如 "它是什么?" 和 "你能用它做什么?" 之类的问题,并确保你熟悉 JavaScript 的用途。

¥Welcome to the MDN beginner's JavaScript course! In this article we will look at JavaScript from a high level, answering questions such as "What is it?" and "What can you do with it?", and making sure you are comfortable with JavaScript's purpose.

先决条件: 对 HTML 和 CSS 有基本的了解。
目标: 熟悉 JavaScript 是什么、它能做什么以及它如何融入网站。

高级定义

¥A high-level definition

JavaScript 是一种脚本或编程语言,允许你在网页上实现复杂的功能 - 每次网页所做的不仅仅是坐在那里并显示静态信息供你查看 - 显示及时的内容更新、交互式地图、动画 2D/ 3D 图形、滚动视频点唱机等 — 你可以打赌可能涉及 JavaScript。它是标准 Web 技术的第三层,其中两层(HTMLCSS)我们已在学习区域的其他部分中更详细地介绍过。

¥JavaScript is a scripting or programming language that allows you to implement complex features on web pages — every time a web page does more than just sit there and display static information for you to look at — displaying timely content updates, interactive maps, animated 2D/3D graphics, scrolling video jukeboxes, etc. — you can bet that JavaScript is probably involved. It is the third layer of the layer cake of standard web technologies, two of which (HTML and CSS) we have covered in much more detail in other parts of the Learning Area.

The three layers of standard web technologies; HTML, CSS and JavaScript

  • HTML 是我们用来构建 Web 内容并赋予其含义的标记语言,例如定义段落、标题和数据表,或在页面中嵌入图片和视频。
  • CSS 是一种样式规则语言,我们用它来将样式应用于 HTML 内容,例如设置背景颜色和字体,以及在多列中布局内容。
  • JavaScript 是一种脚本语言,使你能够创建动态更新的内容、控制多媒体、动画图片以及几乎所有其他内容。(好吧,虽然不是全部,但是用几行 JavaScript 代码就能实现的效果真是令人惊奇。)

这三层很好地相互叠加。我们以按钮为例。我们可以使用 HTML 对其进行标记,以赋予其结构和用途:

¥The three layers build on top of one another nicely. Let's take a button as an example. We can mark it up using HTML to give it structure and purpose:

html
<button type="button">Player 1: Chris</button>

Button showing Player 1: Chris with no styling

然后我们可以添加一些 CSS 到混合中,让它看起来更漂亮:

¥Then we can add some CSS into the mix to get it looking nice:

css
button {
  font-family: "helvetica neue", helvetica, sans-serif;
  letter-spacing: 1px;
  text-transform: uppercase;
  border: 2px solid rgb(200 200 0 / 60%);
  background-color: rgb(0 217 217 / 60%);
  color: rgb(100 0 0 / 100%);
  box-shadow: 1px 1px 2px rgb(0 0 200 / 40%);
  border-radius: 10px;
  padding: 3px 10px;
  cursor: pointer;
}

Button showing Player 1: Chris with styling

最后,我们可以添加一些 JavaScript 来实现动态行为:

¥And finally, we can add some JavaScript to implement dynamic behavior:

js
const button = document.querySelector("button");

button.addEventListener("click", updateName);

function updateName() {
  const name = prompt("Enter a new name");
  button.textContent = `Player 1: ${name}`;
}

尝试单击文本标签的最后一个版本,看看会发生什么(另请注意,你可以在 GitHub 上找到此演示 - 请参阅 源代码实时运行)!

¥Try clicking on this last version of the text label to see what happens (note also that you can find this demo on GitHub — see the source code, or run it live)!

JavaScript 可以做的事情远不止这些 - 让我们更详细地探讨一下。

¥JavaScript can do a lot more than that — let's explore what in more detail.

那么它到底能做什么呢?

¥So what can it really do?

核心客户端 JavaScript 语言包含一些常见的编程功能,允许你执行以下操作:

¥The core client-side JavaScript language consists of some common programming features that allow you to do things like:

  • 在变量中存储有用的值。例如,在上面的示例中,我们要求输入一个新名称,然后将该名称存储在名为 name 的变量中。
  • 对文本片段的操作(编程中称为 "strings")。在上面的示例中,我们采用字符串“Player 1:" 并将其连接到 name 变量以创建完整的文本标签,例如“玩家 1:克里斯”。
  • 运行代码以响应网页上发生的某些事件。我们在上面的示例中使用 click 事件来检测标签何时被单击,然后运行更新文本标签的代码。
  • 以及更多!

然而,更令人兴奋的是构建在客户端 JavaScript 语言之上的功能。所谓的应用编程接口 (API) 为你提供了在 JavaScript 代码中使用的额外超能力。

¥What is even more exciting however is the functionality built on top of the client-side JavaScript language. So-called Application Programming Interfaces (APIs) provide you with extra superpowers to use in your JavaScript code.

API 是现成的代码构建块集,允许开发者实现原本很难或不可能实现的程序。它们在编程方面的作用与现成的家具套件在住宅架构中的作用相同 - 采用现成的面板并将它们拧在一起制成书架比自己制定设计要容易得多,去找 正确的木材,将所有面板切割成正确的尺寸和形状,找到正确尺寸的螺钉,然后将它们组合在一起制成书架。

¥APIs are ready-made sets of code building blocks that allow a developer to implement programs that would otherwise be hard or impossible to implement. They do the same thing for programming that ready-made furniture kits do for home building — it is much easier to take ready-cut panels and screw them together to make a bookshelf than it is to work out the design yourself, go and find the correct wood, cut all the panels to the right size and shape, find the correct-sized screws, and then put them together to make a bookshelf.

它们通常分为两类。

¥They generally fall into two categories.

Two categories of API; 3rd party APIs are shown to the side of the browser and browser APIs are in the browser

浏览器 API 内置于你的 Web 浏览器中,能够公开来自周围计算机环境的数据,或执行有用的复杂操作。例如:

¥Browser APIs are built into your web browser, and are able to expose data from the surrounding computer environment, or do useful complex things. For example:

  • DOM (Document Object Model) API 允许你操作 HTML 和 CSS,创建、删除和更改 HTML,动态地将新样式应用到页面等。每次你看到页面上出现弹出窗口或显示一些新内容时(如我们在上面看到的) 我们的简单演示)例如,这就是正在运行的 DOM。
  • Geolocation API 检索地理信息。这就是 谷歌地图 找到你的位置并将其绘制在地图上的方式。
  • CanvasWebGL API 允许你创建动画 2D 和 3D 图形。人们正在使用这些网络技术做一些令人惊奇的事情 - 请参阅 Chrome 实验webglsamples
  • HTMLMediaElementWebRTC 一样,音频和视频 API 允许你使用多媒体做一些非常有趣的事情,例如在网页中播放音频和视频,或者从你的网络摄像头抓取视频并将其显示在其他人的计算机上(尝试我们简单的 快照演示 来了解这个想法 )。

注意:上面的许多演示都无法在较旧的浏览器中运行 - 在进行实验时,最好使用 Firefox、Chrome、Edge 或 Opera 等现代浏览器来运行代码。当你接近交付生产代码(即真实客户将使用的真实代码)时,你将需要更详细地考虑 跨浏览器测试

¥Note: Many of the above demos won't work in an older browser — when experimenting, it's a good idea to use a modern browser like Firefox, Chrome, Edge or Opera to run your code in. You will need to consider cross browser testing in more detail when you get closer to delivering production code (i.e. real code that real customers will use).

默认情况下,第三方 API 并未内置到浏览器中,你通常必须从 Web 上的某个位置获取它们的代码和信息。例如:

¥Third party APIs are not built into the browser by default, and you generally have to grab their code and information from somewhere on the Web. For example:

注意:这些 API 是高级的,我们不会在本模块中介绍其中的任何内容。你可以在我们的 客户端 Web API 模块 中找到有关这些的更多信息。

¥Note: These APIs are advanced, and we'll not be covering any of these in this module. You can find out much more about these in our Client-side web APIs module.

还有更多可用的内容!不过,现在还不要兴奋过度。在学习 JavaScript 24 小时后,你将无法构建下一个 Facebook、Google 地图或 Instagram - 首先需要了解很多基础知识。这就是你在这里的原因 - 让我们继续前进!

¥There's a lot more available, too! However, don't get over excited just yet. You won't be able to build the next Facebook, Google Maps, or Instagram after studying JavaScript for 24 hours — there are a lot of basics to cover first. And that's why you're here — let's move on!

JavaScript 在你的页面上做什么?

¥What is JavaScript doing on your page?

在这里,我们实际上将开始查看一些代码,同时探索在页面中运行某些 JavaScript 时实际发生的情况。

¥Here we'll actually start looking at some code, and while doing so, explore what actually happens when you run some JavaScript in your page.

让我们简要回顾一下在浏览器中加载网页时发生的情况(在我们的 CSS 的工作原理 文章中首先讨论过)。当你在浏览器中加载网页时,你正在执行环境(浏览器选项卡)内运行代码(HTML、CSS 和 JavaScript)。这就像一个工厂,接收原材料(代码)并输出产品(网页)。

¥Let's briefly recap the story of what happens when you load a web page in a browser (first talked about in our How CSS works article). When you load a web page in your browser, you are running your code (the HTML, CSS, and JavaScript) inside an execution environment (the browser tab). This is like a factory that takes in raw materials (the code) and outputs a product (the web page).

HTML, CSS and JavaScript code come together to create the content in the browser tab when the page is loaded

JavaScript 的一个非常常见的用途是通过文档对象模型 API(如上所述)动态修改 HTML 和 CSS 以更新用户界面。请注意,Web 文档中的代码通常按照其在页面上出现的顺序加载和执行。如果 JavaScript 在要修改的 HTML 和 CSS 之前加载和运行,则可能会发生错误。你将在本文后面的 脚本加载策略 部分中了解解决此问题的方法。

¥A very common use of JavaScript is to dynamically modify HTML and CSS to update a user interface, via the Document Object Model API (as mentioned above). Note that the code in your web documents is generally loaded and executed in the order it appears on the page. Errors may occur if JavaScript is loaded and run before the HTML and CSS that it is intended to modify. You will learn ways around this later in the article, in the Script loading strategies section.

浏览器安全

¥Browser security

每个浏览器选项卡都有自己单独的存储桶,用于在其中运行代码(这些存储桶在技术术语中称为 "执行环境") - 这意味着在大多数情况下,每个选项卡中的代码完全独立运行,并且一个选项卡中的代码不能直接影响代码 在另一个选项卡中 - 或在另一个网站上。这是一个很好的安全措施 - 如果不是这种情况,那么盗版者可能会开始编写代码来窃取其他网站的信息,以及其他类似的坏事。

¥Each browser tab has its own separate bucket for running code in (these buckets are called "execution environments" in technical terms) — this means that in most cases the code in each tab is run completely separately, and the code in one tab cannot directly affect the code in another tab — or on another website. This is a good security measure — if this were not the case, then pirates could start writing code to steal information from other websites, and other such bad things.

注意:有多种方法可以安全地在不同网站/选项卡之间发送代码和数据,但这些是我们在本课程中不会介绍的高级技术。

¥Note: There are ways to send code and data between different websites/tabs in a safe manner, but these are advanced techniques that we won't cover in this course.

JavaScript 运行顺序

¥JavaScript running order

当浏览器遇到 JavaScript 块时,它通常会按顺序从上到下运行它。这意味着你需要小心放置物品的顺序。例如,让我们回到第一个示例中看到的 JavaScript 块:

¥When the browser encounters a block of JavaScript, it generally runs it in order, from top to bottom. This means that you need to be careful what order you put things in. For example, let's return to the block of JavaScript we saw in our first example:

js
const button = document.querySelector("button");

button.addEventListener("click", updateName);

function updateName() {
  const name = prompt("Enter a new name");
  button.textContent = `Player 1: ${name}`;
}

在这里,我们选择一个按钮(第 1 行),然后向其附加一个事件监听器(第 3 行),以便在单击该按钮时运行 updateName() 代码块(第 5-8 行)。updateName() 代码块(这些类型的可重用代码块称为 "functions")要求用户输入新名称,然后将该名称插入到按钮文本中以更新显示。

¥Here we are selecting a button (line 1), then attaching an event listener to it (line 3) so that when the button is clicked, the updateName() code block (lines 5–8) is run. The updateName() code block (these types of reusable code blocks are called "functions") asks the user for a new name, and then inserts that name into the button text to update the display.

如果交换前两行代码的顺序,它将不再起作用 - 相反,你会在 浏览器开发者控制台 - Uncaught ReferenceError: Cannot access 'button' before initialization 中返回错误。这意味着 button 对象还没有被初始化,所以我们不能给它添加事件监听器。

¥If you swapped the order of the first two lines of code, it would no longer work — instead, you'd get an error returned in the browser developer consoleUncaught ReferenceError: Cannot access 'button' before initialization. This means that the button object has not been initialized yet, so we can't add an event listener to it.

注意:这是一个非常常见的错误 - 在尝试对代码中引用的对象进行操作之前,你需要小心它们是否存在。

¥Note: This is a very common error — you need to be careful that the objects referenced in your code exist before you try to do stuff to them.

解释代码与编译代码

¥Interpreted versus compiled code

你可能会听到在编程环境中解释和编译的术语。在解释型语言中,代码是从上到下运行的,并且立即返回代码运行的结果。在浏览器运行代码之前,你不必将代码转换为不同的形式。代码以程序员友好的文本形式接收并直接进行处理。

¥You might hear the terms interpreted and compiled in the context of programming. In interpreted languages, the code is run from top to bottom and the result of running the code is immediately returned. You don't have to transform the code into a different form before the browser runs it. The code is received in its programmer-friendly text form and processed directly from that.

另一方面,编译语言在由计算机运行之前会被转换(编译)为另一种形式。例如,C/C++ 被编译成机器代码,然后由计算机运行。该程序以二进制格式执行,该二进制格式是根据原始程序源代码生成的。

¥Compiled languages on the other hand are transformed (compiled) into another form before they are run by the computer. For example, C/C++ are compiled into machine code that is then run by the computer. The program is executed from a binary format, which was generated from the original program source code.

JavaScript 是一种轻量级的解释性编程语言。Web 浏览器接收原始文本形式的 JavaScript 代码并从中运行脚本。从技术角度来看,大多数现代 JavaScript 解释器实际上使用一种称为即时编译的技术来提高性能;JavaScript 源代码在使用脚本时被编译成更快的二进制格式,以便它可以尽快运行。然而,JavaScript 仍然被认为是一种解释性语言,因为编译是在运行时处理的,而不是提前处理的。

¥JavaScript is a lightweight interpreted programming language. The web browser receives the JavaScript code in its original text form and runs the script from that. From a technical standpoint, most modern JavaScript interpreters actually use a technique called just-in-time compiling to improve performance; the JavaScript source code gets compiled into a faster, binary format while the script is being used, so that it can be run as quickly as possible. However, JavaScript is still considered an interpreted language, since the compilation is handled at run time, rather than ahead of time.

两种语言都有优点,但我们现在不讨论它们。

¥There are advantages to both types of language, but we won't discuss them right now.

服务器端与客户端代码

¥Server-side versus client-side code

你可能还听说过服务器端和客户端代码这两个术语,尤其是在 Web 开发环境中。客户端代码是在用户计算机上运行的代码 - 当查看网页时,会下载该页面的客户端代码,然后由浏览器运行并显示。在本模块中,我们将明确讨论客户端 JavaScript。

¥You might also hear the terms server-side and client-side code, especially in the context of web development. Client-side code is code that is run on the user's computer — when a web page is viewed, the page's client-side code is downloaded, then run and displayed by the browser. In this module we are explicitly talking about client-side JavaScript.

另一方面,服务器端代码在服务器上运行,然后下载其结果并显示在浏览器中。流行的服务器端 Web 语言的示例包括 PHP、Python、Ruby、ASP.NET,甚至 JavaScript!JavaScript 还可以用作服务器端语言,例如在流行的 Node.js 环境中 - 你可以在我们的 动态网站 – 服务器端编程 主题中找到有关服务器端 JavaScript 的更多信息。

¥Server-side code on the other hand is run on the server, then its results are downloaded and displayed in the browser. Examples of popular server-side web languages include PHP, Python, Ruby, ASP.NET, and even JavaScript! JavaScript can also be used as a server-side language, for example in the popular Node.js environment — you can find out more about server-side JavaScript in our Dynamic Websites – Server-side programming topic.

动态代码与静态代码

¥Dynamic versus static code

动态一词用于描述客户端 JavaScript 和服务器端语言 - 它指的是更新网页/应用的显示以在不同情况下显示不同内容的能力,并根据需要生成新内容。服务器端代码在服务器上动态生成新内容,例如 从数据库中提取数据,而客户端 JavaScript 在客户端的浏览器中动态生成新内容,例如 创建一个新的 HTML 表,用服务器请求的数据填充该表,然后在向用户显示的网页中显示该表。两种上下文中的含义略有不同,但相关,并且两种方法(服务器端和客户端)通常一起工作。

¥The word dynamic is used to describe both client-side JavaScript, and server-side languages — it refers to the ability to update the display of a web page/app to show different things in different circumstances, generating new content as required. Server-side code dynamically generates new content on the server, e.g. pulling data from a database, whereas client-side JavaScript dynamically generates new content inside the browser on the client, e.g. creating a new HTML table, filling it with data requested from the server, then displaying the table in a web page shown to the user. The meaning is slightly different in the two contexts, but related, and both approaches (server-side and client-side) usually work together.

没有动态更新内容的网页被称为静态网页 - 它只是始终显示相同的内容。

¥A web page with no dynamically updating content is referred to as static — it just shows the same content all the time.

如何将 JavaScript 添加到你的页面?

¥How do you add JavaScript to your page?

JavaScript 以与 CSS 类似的方式应用于 HTML 页面。CSS 使用 <link> 元素来应用外部样式表,使用 <style> 元素来应用 HTML 内部样式表,而 JavaScript 在 HTML 世界中只需要一个朋友 - <script> 元素。让我们了解一下这是如何工作的。

¥JavaScript is applied to your HTML page in a similar manner to CSS. Whereas CSS uses <link> elements to apply external stylesheets and <style> elements to apply internal stylesheets to HTML, JavaScript only needs one friend in the world of HTML — the <script> element. Let's learn how this works.

内部 JavaScript

¥Internal JavaScript

  1. 首先,制作示例文件 apply-javascript.html.txt 的本地副本。将其保存在合适的目录中。
  2. 在 Web 浏览器和文本编辑器中打开该文件。你将看到 HTML 创建了一个包含可单击按钮的简单网页。
  3. 接下来,转到文本编辑器并在头部添加以下内容 - 就在结束 </head> 标记之前:
    html
    <script>
      // JavaScript goes here
    </script>
    
  4. 现在,我们将在 <script> 元素中添加一些 JavaScript,以使页面做一些更有趣的事情 - 在 "// JavaScript goes here" 行下方添加以下代码:
    js
    document.addEventListener("DOMContentLoaded", () => {
      function createParagraph() {
        const para = document.createElement("p");
        para.textContent = "You clicked the button!";
        document.body.appendChild(para);
      }
    
      const buttons = document.querySelectorAll("button");
    
      for (const button of buttons) {
        button.addEventListener("click", createParagraph);
      }
    });
    
  5. 保存文件并刷新浏览器 - 现在你应该看到,当你单击该按钮时,会生成一个新段落并放置在下面。

注意:如果你的示例似乎不起作用,请再次执行这些步骤并检查你是否所做的一切正确。你是否将起始代码的本地副本保存为 .html 文件?你是否在 </head> 标签之前添加了 <script> 元素?你输入的 JavaScript 是否完全按照所示方式输入?JavaScript 区分大小写,并且非常挑剔,因此你需要完全按照所示输入语法,否则可能无法工作。

¥Note: If your example doesn't seem to work, go through the steps again and check that you did everything right. Did you save your local copy of the starting code as a .html file? Did you add your <script> element just before the </head> tag? Did you enter the JavaScript exactly as shown? JavaScript is case sensitive, and very fussy, so you need to enter the syntax exactly as shown, otherwise it may not work.

注意:你可以在 GitHub 上看到此版本为 apply-javascript-internal.html (也看到现场直播)。

¥Note: You can see this version on GitHub as apply-javascript-internal.html (see it live too).

外部 JavaScript

¥External JavaScript

这很好用,但是如果我们想将 JavaScript 放在外部文件中怎么办?现在让我们来探讨一下。

¥This works great, but what if we wanted to put our JavaScript in an external file? Let's explore this now.

  1. 首先,在示例 HTML 文件所在的目录中创建一个新文件。将其命名为 script.js — 确保它具有 .js 文件扩展名,因为这就是它被识别为 JavaScript 的方式。
  2. 将当前的 <script> 元素替换为以下内容:
    html
    <script src="script.js" defer></script>
    
  3. script.js 内,添加以下脚本:
    js
    function createParagraph() {
      const para = document.createElement("p");
      para.textContent = "You clicked the button!";
      document.body.appendChild(para);
    }
    
    const buttons = document.querySelectorAll("button");
    
    for (const button of buttons) {
      button.addEventListener("click", createParagraph);
    }
    
  4. 保存并刷新浏览器,你应该会看到相同的内容!它的工作原理是一样的,但现在我们已经将 JavaScript 存储在外部文件中。对于组织代码并使其可在多个 HTML 文件中重用而言,这通常是一件好事。另外,HTML 更易于阅读,无需在其中转储大量脚本。

注意:你可以在 GitHub 上看到此版本为 apply-javascript-external.htmlscript.js (也看到现场直播)。

¥Note: You can see this version on GitHub as apply-javascript-external.html and script.js (see it live too).

内联 JavaScript 处理程序

¥Inline JavaScript handlers

请注意,有时你会遇到 HTML 中的一些实际 JavaScript 代码。它可能看起来像这样:

¥Note that sometimes you'll come across bits of actual JavaScript code living inside HTML. It might look something like this:

js
function createParagraph() {
  const para = document.createElement("p");
  para.textContent = "You clicked the button!";
  document.body.appendChild(para);
}
html
<button onclick="createParagraph()">Click me!</button>

你可以尝试下面这个版本的演示。

¥You can try this version of our demo below.

该演示与前两节具有完全相同的功能,只是 <button> 元素包含一个内联 onclick 处理程序,以使该函数在按下按钮时运行。

¥This demo has exactly the same functionality as in the previous two sections, except that the <button> element includes an inline onclick handler to make the function run when the button is pressed.

但是,请不要这样做。用 JavaScript 污染 HTML 是不好的做法,而且效率很低 — 你必须在希望 JavaScript 应用的每个按钮上包含 onclick="createParagraph()" 属性。

¥Please don't do this, however. It is bad practice to pollute your HTML with JavaScript, and it is inefficient — you'd have to include the onclick="createParagraph()" attribute on every button you want the JavaScript to apply to.

使用 addEventListener 代替

¥Using addEventListener instead

不要在 HTML 中包含 JavaScript,而是使用纯 JavaScript 构造。querySelectorAll() 功能允许你选择页面上的所有按钮。然后,你可以循环遍历按钮,使用 addEventListener() 为每个按钮分配一个处理程序。其代码如下所示:

¥Instead of including JavaScript in your HTML, use a pure JavaScript construct. The querySelectorAll() function allows you to select all the buttons on a page. You can then loop through the buttons, assigning a handler for each using addEventListener(). The code for this is shown below:

js
const buttons = document.querySelectorAll("button");

for (const button of buttons) {
  button.addEventListener("click", createParagraph);
}

这可能比 onclick 属性长一点,但它适用于所有按钮 - 无论页面上有多少个按钮,也不管添加或删除了多少个按钮。JavaScript 不需要更改。

¥This might be a bit longer than the onclick attribute, but it will work for all buttons — no matter how many are on the page, nor how many are added or removed. The JavaScript does not need to be changed.

注意:尝试编辑你的 apply-javascript.html 版本并向文件中添加更多按钮。当你重新加载时,你应该发现单击所有按钮都会创建一个段落。整洁吧?

¥Note: Try editing your version of apply-javascript.html and add a few more buttons into the file. When you reload, you should find that all of the buttons when clicked will create a paragraph. Neat, huh?

脚本加载策略

¥Script loading strategies

在正确的时间加载脚本涉及许多问题。没有什么事情像看上去那么简单!一个常见的问题是页面上的所有 HTML 都是按照其出现的顺序加载的。如果你使用 JavaScript 来操作页面上的元素(或更准确地说,是 文档对象模型),那么如果 JavaScript 在你尝试执行操作的 HTML 之前加载和解析,你的代码将无法工作。

¥There are a number of issues involved with getting scripts to load at the right time. Nothing is as simple as it seems! A common problem is that all the HTML on a page is loaded in the order in which it appears. If you are using JavaScript to manipulate elements on the page (or more accurately, the Document Object Model), your code won't work if the JavaScript is loaded and parsed before the HTML you are trying to do something to.

在上面的代码示例中,在内部和外部示例中,JavaScript 在解析 HTML 主体之前在文档的头部加载并运行。这可能会导致错误,因此我们使用了一些构造来解决它。

¥In the above code examples, in the internal and external examples the JavaScript is loaded and run in the head of the document, before the HTML body is parsed. This could cause an error, so we've used some constructs to get around it.

在内部示例中,你可以看到代码周围的结构:

¥In the internal example, you can see this structure around the code:

js
document.addEventListener("DOMContentLoaded", () => {
  // …
});

这是一个事件监听器,监听浏览器的 DOMContentLoaded 事件,该事件表示 HTML body 已完全加载并解析。该块内的 JavaScript 直到该事件被触发后才会运行,因此可以避免错误(你将在课程的后面部分了解 了解事件)。

¥This is an event listener, which listens for the browser's DOMContentLoaded event, which signifies that the HTML body is completely loaded and parsed. The JavaScript inside this block will not run until after that event is fired, therefore the error is avoided (you'll learn about events later in the course).

在外部示例中,我们使用更现代的 JavaScript 功能来解决问题,即 defer 属性,它告诉浏览器在到达 <script> 标记元素后继续下载 HTML 内容。

¥In the external example, we use a more modern JavaScript feature to solve the problem, the defer attribute, which tells the browser to continue downloading the HTML content once the <script> tag element has been reached.

html
<script src="script.js" defer></script>

在这种情况下,脚本和 HTML 将同时加载,并且代码将正常工作。

¥In this case both the script and the HTML will load simultaneously and the code will work.

注意:在外部情况下,我们不需要使用 DOMContentLoaded 事件,因为 defer 属性为我们解决了这个问题。我们没有对内部 JavaScript 示例使用 defer 解决方案,因为 defer 仅适用于外部脚本。

¥Note: In the external case, we did not need to use the DOMContentLoaded event because the defer attribute solved the problem for us. We didn't use the defer solution for the internal JavaScript example because defer only works for external scripts.

解决此问题的一种老式解决方案是将脚本元素放在正文的底部(例如,在 </body> 标记之前),以便在解析所有 HTML 后加载它。此解决方案的问题在于,在加载 HTML DOM 之前,脚本的加载/解析会被完全阻止。在包含大量 JavaScript 的大型网站上,这可能会导致严重的性能问题,从而降低网站速度。

¥An old-fashioned solution to this problem used to be to put your script element right at the bottom of the body (e.g. just before the </body> tag), so that it would load after all the HTML has been parsed. The problem with this solution is that loading/parsing of the script is completely blocked until the HTML DOM has been loaded. On larger sites with lots of JavaScript, this can cause a major performance issue, slowing down your site.

异步和延迟

¥async and defer

实际上,我们可以使用两个现代功能来绕过阻塞脚本的问题 - asyncdefer(我们在上面看到)。我们来看看这两者之间的区别。

¥There are actually two modern features we can use to bypass the problem of the blocking script — async and defer (which we saw above). Let's look at the difference between these two.

使用 async 属性加载的脚本将下载脚本,而不会在获取脚本时阻塞页面。但是,一旦下载完成,脚本就会执行,这会阻止页面呈现。这意味着在脚本执行完成之前,将阻止处理网页上的其余内容并向用户显示。你无法保证脚本将以任何特定顺序运行。当页面中的脚本彼此独立运行且不依赖于页面上的其他脚本时,最好使用 async

¥Scripts loaded using the async attribute will download the script without blocking the page while the script is being fetched. However, once the download is complete, the script will execute, which blocks the page from rendering. This means that the rest of the content on the web page is prevented from being processed and displayed to the user until the script finishes executing. You get no guarantee that scripts will run in any specific order. It is best to use async when the scripts in the page run independently from each other and depend on no other script on the page.

使用 defer 属性加载的脚本将按照它们在页面上出现的顺序加载。它们只有在页面内容全部加载后才会运行,如果你的脚本依赖于就位的 DOM(例如,它们修改页面上的一个或多个元素),这将非常有用。

¥Scripts loaded with the defer attribute will load in the order they appear on the page. They won't run until the page content has all loaded, which is useful if your scripts depend on the DOM being in place (e.g. they modify one or more elements on the page).

以下是不同脚本加载方法的直观表示以及这对你的页面意味着什么:

¥Here is a visual representation of the different script loading methods and what that means for your page:

How the three script loading method work: default has parsing blocked while JavaScript is fetched and executed. With async, the parsing pauses for execution only. With defer, parsing isn't paused, but execution on happens after everything is else is parsed.

该图片来自 HTML 规范,根据 CC BY 4.0 许可条款复制并裁剪为缩小版本。

¥This image is from the HTML spec, copied and cropped to a reduced version, under CC BY 4.0 license terms.

例如,如果你有以下脚本元素:

¥For example, if you have the following script elements:

html
<script async src="js/vendor/jquery.js"></script>

<script async src="js/script2.js"></script>

<script async src="js/script3.js"></script>

你不能依赖脚本加载的顺序。jquery.js 可能在 script2.jsscript3.js 之前或之后加载,如果是这种情况,这些脚本中依赖于 jquery 的任何函数都将产生错误,因为在脚本运行时不会定义 jquery

¥You can't rely on the order the scripts will load in. jquery.js may load before or after script2.js and script3.js and if this is the case, any functions in those scripts depending on jquery will produce an error because jquery will not be defined at the time the script runs.

当你有一堆后台脚本需要加载,并且你只想尽快将它们安装到位时,应该使用 async。例如,也许你有一些游戏数据文件需要加载,游戏实际开始时需要这些数据文件,但现在你只想继续显示游戏简介、标题和大厅,而不会被脚本加载阻止 。

¥async should be used when you have a bunch of background scripts to load in, and you just want to get them in place as soon as possible. For example, maybe you have some game data files to load, which will be needed when the game actually begins, but for now you just want to get on with showing the game intro, titles, and lobby, without them being blocked by script loading.

使用 defer 属性加载的脚本(见下文)将按照它们在页面中出现的顺序运行,并在脚本和内容下载后立即执行它们:

¥Scripts loaded using the defer attribute (see below) will run in the order they appear in the page and execute them as soon as the script and content are downloaded:

html
<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>

在第二个示例中,我们可以确定 jquery.js 将在 script2.jsscript3.js 之前加载,并且 script2.js 将在 script3.js 之前加载。它们只有在页面内容全部加载后才会运行,如果你的脚本依赖于就位的 DOM(例如,它们修改页面上的一个或多个元素),这将非常有用。

¥In the second example, we can be sure that jquery.js will load before script2.js and script3.js and that script2.js will load before script3.js. They won't run until the page content has all loaded, which is useful if your scripts depend on the DOM being in place (e.g. they modify one or more elements on the page).

总结一下:

¥To summarize:

  • asyncdefer 都指示浏览器在单独的线程中下载脚本,而页面的其余部分(DOM 等)正在下载,因此在获取过程中页面加载不会被阻止。
  • 下载完成后,具有 async 属性的脚本将立即执行。这会阻塞页面并且不保证任何特定的执行顺序。
  • 具有 defer 属性的脚本将按照它们所在的顺序加载,并且只有在所有内容加载完成后才会执行。
  • 如果你的脚本应立即运行并且它们没有任何依赖,则使用 async
  • 如果你的脚本需要等待解析并依赖于其他脚本和/或 DOM,请使用 defer 加载它们,并按照你希望浏览器执行它们的顺序放置其相应的 <script> 元素。

评论

¥Comments

与 HTML 和 CSS 一样,可以在 JavaScript 代码中写入注释,这些注释将被浏览器忽略,并且可以向你的其他开发者提供有关代码如何工作的说明(以及你,如果你在之后返回代码) 六个月了,不记得你做了什么)。注释非常有用,你应该经常使用它们,特别是对于较大的应用。有两种类型:

¥As with HTML and CSS, it is possible to write comments into your JavaScript code that will be ignored by the browser, and exist to provide instructions to your fellow developers on how the code works (and you, if you come back to your code after six months and can't remember what you did). Comments are very useful, and you should use them often, particularly for larger applications. There are two types:

  • A single line comment is written after a double forward slash (//), e.g.
    js
    // I am a comment
    
  • 多行注释写在字符串 /* 和 */ 之间,例如
    js
    /*
      I am also
      a comment
    */
    

例如,我们可以使用如下注释来注释上一个演示的 JavaScript:

¥So for example, we could annotate our last demo's JavaScript with comments like so:

js
// Function: creates a new paragraph and appends it to the bottom of the HTML body.

function createParagraph() {
  const para = document.createElement("p");
  para.textContent = "You clicked the button!";
  document.body.appendChild(para);
}

/*
  1. Get references to all the buttons on the page in an array format.
  2. Loop through all the buttons and add a click event listener to each one.

  When any button is pressed, the createParagraph() function will be run.
*/

const buttons = document.querySelectorAll("button");

for (const button of buttons) {
  button.addEventListener("click", createParagraph);
}

注意:一般来说,更多的注释通常比更少的注释更好,但是如果你发现自己添加了大量注释来解释变量是什么(你的变量名称可能应该更直观),或者解释非常简单的操作(也许你的代码是 过于复杂)。

¥Note: In general more comments are usually better than less, but you should be careful if you find yourself adding lots of comments to explain what variables are (your variable names perhaps should be more intuitive), or to explain very simple operations (maybe your code is overcomplicated).

概括

¥Summary

现在你已经迈出了进入 JavaScript 世界的第一步。我们从理论开始,让你习惯为什么要使用 JavaScript 以及可以用它做什么。在此过程中,你看到了一些代码示例,并了解了 JavaScript 如何与网站上的其余代码配合,等等。

¥So there you go, your first step into the world of JavaScript. We've begun with just theory, to start getting you used to why you'd use JavaScript and what kind of things you can do with it. Along the way, you saw a few code examples and learned how JavaScript fits in with the rest of the code on your website, amongst other things.

JavaScript 现在可能看起来有点令人畏惧,但不用担心 - 在本课程中,我们将通过简单的步骤引导你完成它,这对于以后的学习是有意义的。在下一篇文章中,我们将 直接投入到实际工作中,让你直接开始构建自己的 JavaScript 示例。

¥JavaScript may seem a bit daunting right now, but don't worry — in this course, we will take you through it in simple steps that will make sense going forward. In the next article, we will plunge straight into the practical, getting you to jump straight in and build your own JavaScript examples.