什么是 JavaScript?

欢迎来到 MDN 初学者 JavaScript 课程!在本文中,我们将从宏观角度了解 JavaScript,回答诸如“它是什么?”和“你能用它做什么?”等问题,并确保你对 JavaScript 的目的感到满意。

预备知识 了解 HTMLCSS 基础知识
学习目标
  • 什么是 JavaScript,以及它如何融入网站。
  • 你能用 JavaScript 做什么。
  • 将 JavaScript 添加到网页中。
  • 在 JavaScript 中编写注释。

高级定义

JavaScript 是一种脚本或编程语言,允许你在网页上实现复杂的功能——每当网页不仅仅是静态地显示信息供你查看时——比如显示及时内容更新、交互式地图、动画 2D/3D 图形、滚动视频点唱机等——你都可以肯定 JavaScript 可能参与其中。它是标准 Web 技术“分层蛋糕”的第三层,其中两层(HTMLCSS)我们已经在学习区其他部分更详细地介绍过。

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

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

这三层相互良好地构建。让我们以一个按钮为例。我们可以使用 HTML 对其进行标记以赋予其结构和目的

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

Button showing Player 1: Chris with no styling

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

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 来实现动态行为

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

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

button.addEventListener("click", updateName);

你可以点击“播放”在 MDN Playground 中查看和编辑示例。尝试点击文本标签看看会发生什么。

JavaScript 能做的远不止这些——让我们更详细地探讨一下。

注意:在继续之前,何不现在就动手尝试 Scrimba 的一项挑战?查看 渲染欢迎消息 MDN 学习伙伴。如果你不知道如何编写此代码,完全不用担心;你可以尝试进行一些网络搜索以找到答案,或者在教程结束时查看解决方案。

那么它到底能做什么?

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

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

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

API 是一组现成的代码构建块,允许开发人员实现原本难以或不可能实现的程序。它们在编程方面所做的事情,就像现成的家具套件在房屋建造方面所做的事情一样——与其自己设计、寻找合适的木材、将所有面板切割成正确的尺寸和形状、找到正确尺寸的螺丝,然后组装成一个书架,不如直接使用现成的面板并将它们拧在一起,这样要容易得多。

它们通常分为两类。

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

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

  • DOM(文档对象模型)API 允许你操作 HTML 和 CSS,创建、删除和更改 HTML,动态地将新样式应用于你的页面等等。每当你在页面上看到弹出窗口出现,或显示一些新内容(如我们在上面的简单演示中看到的那样)时,那就是 DOM 在发挥作用。
  • 地理定位 API 检索地理信息。这就是 Google 地图 能够找到你的位置并将其绘制在地图上的方式。
  • CanvasWebGL API 允许你创建动画 2D 和 3D 图形。人们正在使用这些 Web 技术做一些令人惊奇的事情——请参阅 Chrome 实验webglsamples
  • 音频和视频 API,如 HTMLMediaElementWebRTC,允许你使用多媒体做一些非常有趣的事情,例如直接在网页中播放音频和视频,或者从你的网络摄像头抓取视频并将其显示在别人的计算机上(尝试我们的简单 快照演示 以了解其思想)。

第三方 API 默认不内置在浏览器中,你通常必须从 Web 上的某个地方获取它们的代码和信息。例如:

注意:这些 API 是高级的,我们不会在本模块中涵盖任何这些 API。你可以在我们的客户端 Web API 模块中找到更多关于它们的信息。

还有更多可用!然而,不要太兴奋。你不会在学习 JavaScript 24 小时后就能构建下一个 Facebook、Google 地图或 Instagram——首先还有很多基础知识需要涵盖。这就是你来这里的原因——让我们继续!

JavaScript 在你的页面上做什么?

在这里,我们将真正开始查看一些代码,同时,探索当你页面中运行一些 JavaScript 时实际会发生什么。

让我们简要回顾一下浏览器加载网页时发生的事情(首次在我们什么是 CSS?文章中提到)。当你在浏览器中加载网页时,你正在执行你的代码(HTML、CSS 和 JavaScript)在一个执行环境(浏览器选项卡)中。这就像一个工厂,接收原材料(代码)并输出产品(网页)。

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

JavaScript 的一个非常常见的用途是,通过文档对象模型 API(如上所述)动态修改 HTML 和 CSS 来更新用户界面。

浏览器安全

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

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

JavaScript 运行顺序

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

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

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

button.addEventListener("click", updateName);

在这里,我们首先定义一个名为 updateName() 的代码块(这些可重用代码块被称为函数),它要求用户输入一个新名称并将该名称插入到按钮的文本中。然后我们使用 document.querySelector 存储对按钮的引用,并使用 addEventListener 为其附加一个事件监听器,以便在点击按钮时,运行 updateName() 函数。

如果你交换 const button = ...button.addEventListener(...) 行的顺序,代码将不再起作用——相反,你会在浏览器开发者控制台中收到一个错误——Uncaught ReferenceError: Cannot access 'button' before initialization。这意味着 button 对象尚未初始化,因此我们无法为其添加事件监听器。

注意:JavaScript 并非总是完全按照从上到下的顺序运行,这取决于像提升这样的行为,但目前,请记住通常需要在使用之前定义项目。这是错误的常见来源。

解释型代码与编译型代码

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

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

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

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

服务器端代码与客户端代码

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

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

动态代码与静态代码

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

没有动态更新内容的网页被称为静态——它总是显示相同的内容。

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

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

注意:Scrimba 的设置我们的 JavaScript 文件MDN 学习伙伴交互式教程演示了几种将 JavaScript 添加到 HTML 的不同方法。

内部 JavaScript

  1. 首先,将我们的示例文件 apply-javascript.html 创建一个本地副本。将其保存到某个合适的目录中。

  2. 在你的网页浏览器和文本编辑器中打开该文件。你会看到 HTML 创建了一个包含一个可点击按钮的简单网页。

  3. 接下来,转到你的文本编辑器,在你的 body 底部——紧靠在结束的 </body> 标签之前添加以下内容

    html
    <script>
      // JavaScript goes here
    </script>
    

    请注意,你的 Web 文档中的代码通常按照它们在页面上出现的顺序加载和执行。通过将 JavaScript 放在底部,我们确保所有 HTML 元素都已加载。(另请参阅下面的脚本加载策略。)

  4. 现在我们将在 <script> 元素中添加一些 JavaScript,使页面做一些更有趣的事情——在 "// JavaScript goes here" 行下方添加以下代码

    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);
    }
    
  5. 保存文件并刷新浏览器——现在你应该会看到,当你点击按钮时,会生成一个新的段落并放置在下方。

注意:如果你的示例似乎不起作用,请再次检查步骤,确保你做得一切正确。你是否将起始代码的本地副本保存为 .html 文件?你是否将 <script> 元素添加到 </body> 标签之前?你是否精确输入了 JavaScript,如所示?JavaScript 对大小写敏感,并且非常挑剔,因此你需要精确输入语法,否则它可能无法工作。

注意:你可以在 GitHub 上看到此版本,名为 apply-javascript-internal.html也可在线查看)。

外部 JavaScript

这很好,但如果我想把 JavaScript 放在一个外部文件中呢?让我们现在探讨一下。

  1. 首先,在与你的示例 HTML 文件相同的目录中创建一个新文件。将其命名为 script.js——确保它具有 .js 文件名扩展名,因为这是识别其为 JavaScript 的方式。

  2. 删除 </body> 底部当前的 <script> 元素,并在结束的 </head> 标签之前添加以下内容(这样浏览器可以比在底部时更早地开始加载文件)

    html
    <script type="module" src="script.js"></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. 保存并刷新浏览器。你会发现点击按钮没有效果,如果你检查浏览器的控制台,你会看到类似 Cross-origin request blocked 的错误。这是因为像许多外部资源一样,JavaScript 模块需要从与 HTML 同源加载,而 file:// URL 不符合要求。有两种解决方案可以解决这个问题

    • 我们推荐的解决方案是设置本地测试服务器。运行服务器程序并在端口 8000 上提供 apply-javascript-external.htmlscript.js 文件后,打开浏览器并访问 https://:8000
    • 如果无法运行本地服务器,也可以使用 <script defer src="script.js"></script> 而不是 <script type="module" src="script.js"></script>。有关更多信息,请参阅下面的脚本加载策略。但请注意,教程其他部分使用的功能可能仍需要本地 HTTP 服务器。
  5. 现在网站和以前一样工作,但现在我们的 JavaScript 放在一个外部文件中。这通常在组织代码和使其在多个 HTML 文件中可重用方面是件好事。此外,HTML 更容易阅读,没有大量脚本堆积其中。

注意:你可以在 GitHub 上看到此版本,名为 apply-javascript-external.htmlscript.js也可在线查看)。

内联 JavaScript 处理程序

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

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>

你可以在下面尝试我们这个演示版本。

这个演示与前两节的功能完全相同,不同之处在于 <button> 元素包含一个内联的 onclick 处理程序,以便在按钮按下时运行函数。

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

改为使用 addEventListener

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

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

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

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

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

脚本加载策略

页面上的所有 HTML 都按照它们出现的顺序加载。如果你使用 JavaScript 来操纵页面上的元素(或者更准确地说,是文档对象模型),如果 JavaScript 在你尝试操作的 HTML 之前加载和解析,你的代码将无法工作。

有几种不同的策略可以确保你的 JavaScript 仅在 HTML 被解析后运行

  • 在上面的内部 JavaScript 示例中,脚本元素放置在文档主体的底部,因此仅在 HTML 主体的其余部分解析后才运行。

  • 在上面的外部 JavaScript 示例中,脚本元素放置在文档的头部,在 HTML body 解析之前。但是因为我们使用的是 <script type="module">,代码被视为一个模块,浏览器会等待所有 HTML 处理完毕后才执行 JavaScript 模块。(你也可以将外部脚本放在 body 底部。但是,如果 HTML 很多且网络缓慢,浏览器甚至可能需要很长时间才能开始获取和加载脚本,因此将外部脚本放在 head 中通常更好。)

  • 如果你仍然想在文档头中使用非模块脚本,这可能会阻止整个页面显示,并可能因为在 HTML 解析之前执行而导致错误

    • 对于外部脚本,你应该在 <script> 元素上添加 defer(或者如果你不需要 HTML 准备就绪,则添加 async)属性。
    • 对于内部脚本,你应该将代码封装在 DOMContentLoaded 事件监听器中。

    这一点超出了本教程的范围,但是除非你需要支持非常旧的浏览器,否则你不必这样做,只需使用 <script type="module"> 即可。

注释

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

  • 单行注释写在双斜杠 (//) 之后,例如

    js
    // I am a comment
    
  • 多行注释写在 /**/ 字符串之间,例如

    js
    /*
      I am also
      a comment
    */
    

例如,我们可以这样用注释来注解我们上一个演示的 JavaScript

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);
}

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

总结

就这样,你迈出了进入 JavaScript 世界的第一步。我们从理论开始,让你习惯于为什么以及如何使用 JavaScript。在此过程中,你看到了几个代码示例,并了解了 JavaScript 如何与你网站上的其他代码协同工作,等等。

JavaScript 现在可能看起来有点令人生畏,但别担心——在本课程中,我们将通过简单的步骤带你学习,这些步骤将让你循序渐进。在下一篇文章中,我们将直接进入实践,让你立即动手构建自己的 JavaScript 示例。