JavaScript (缩写:JS)是一门编程语言,它是一门完备的动态编程语言。JavaScript 的应用场合极其广泛,简单到幻灯片、照片库、浮动布局和响应按钮点击,复杂到游戏、2D/3D 动画、大型数据库驱动程序等等。JavaScript 相当简洁,却非常灵活。开发者们基于 JavaScript 核心编写了大量实用工具,可以使 开发工作事半功倍。其中包括:

  • 浏览器应用程序接口(API)—— 浏览器内置的 API 提供了丰富的功能,比如:动态创建 HTML 和设置 CSS 样式、从用户的摄像头采集处理视频流、生成 3D 图像与音频样本等等。
  • 第三方 API —— 让开发者可以在自己的站点中整合其他内容提供者(Twitter、Facebook 等)提供的功能。
  • 第三方框架和库 —— 用来快速构建网站和应用。

这个阶段不会对 JavaScript 语言和上述工具做过多的介绍。之后可以到 JavaScript 学习区 和 MDN 的其他地方学习更多细节。

总之我们知道,javascript应用于 HTML 文档时,可为网站提供动态交互特性,例如:游戏、动态样式、动画以及在按下按钮或收到表单数据时做出的响应等。

首先,来看看如何在页面中添加一些基本的 JavaScript 脚本来建造一个“Hello world!”示例

在 index.html 文件</body> 标签前的新行添加以下代码。与 CSS 的 <link> 元素类似,它将 JavaScript 引入页面以作用于 HTML(以及 CSS 等页面上所有内容)。 <script src="scripts/main.js" defer></script>

备注: 我们将 <script> 放在 HTML 文件的底部附近的原因是浏览器会按照代码在文件中的顺序加载 HTML。如果先加载的 JavaScript 期望修改其下方的 HTML,那么它可能由于 HTML 尚未被加载而失效。因此,将 JavaScript 代码放在 HTML 页面的底部附近通常是最好的策略。

现在将以下代码添加到 main.js 文件中

let myHeading = document.querySelector('h1'); myHeading.textContent = 'Hello world!';

可以看到,JavaScript 把页面的标题改成了“Hello world!” 。首先用 querySelector() 函数获取标题的引用,并把它储存在 myHeading 变量中。这与 CSS 选择器的用法非常相像:若要对某个元素进行操作,首先得选择它。

之后,把 myHeading 变量的属性 textContent (标题内容)修改为“Hello world!” 。

备注: 上面用到的两个函数都来自 文档对象模型 (DOM) API,均用于控制文档。

当然 这是外部链接 而内部scripts只需要将代码添加到<scripts>元素中。

代码结构

条件语句是一种代码结构,用来测试表达式的真假,并根据测试结果运行不同的代码。一个常用的条件语句是 if ... else。下面是一个示例:

let iceCream = 'chocolate';
 if (iceCream === 'chocolate') {
   alert('我最喜欢巧克力冰激淋了。');
 } else {
   alert('但是巧克力才是我的最爱呀……');
 }

函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。如果代码中有一个类似变量名后加小括号 () 的东西,很可能就是一个函数。函数通常包括参数,参数中保存着一些必要的数据。它们位于括号内部,多个参数之间用逗号分开。

上述document.querySelector 和 alert 是浏览器内置的函数,随时可用。比如, alert() 函数在浏览器窗口内弹出一个警告框,还应为其提供一个字符串参数,以告诉它警告框里要显示的内容。

好消息是:人人都能定义自己的函数,如下

function multiply(num1, num2) {
   let result = num1 * num2;
   return result;
 }

事件能为网页添加真实的交互能力。它可以捕捉浏览器操作并运行一些代码做为响应。将事件与元素绑定有许多方法。

最简单的事件是点击事件,鼠标的点击操作会触发该事件。可尝试将下面的代码输入到控制台,然后点击页面的任意位置,会出现弹窗。在这里选用了 <html> 元素,然后调用了它的 addEventListener() 方法,将事件名称('click')以及其回调函数(当事件发生时,调用该函数)传入该函数中作为调用参数。

document.querySelector("html").addEventListener("click", function () {
   alert("别戳我,我怕疼。");
 });

注意,刚刚我们传递给 addEventListener() 的函数被称为匿名函数,因为它没有名字。匿名函数还有另一种我们称之为箭头函数的写法,箭头函数使用 () => 代替 function ()

document.querySelector('html').addEventListener('click', () => {
   alert('别戳我,我怕疼。');
 });

 

现在你已经具备了一些 JavaScript 基础,下面来为示例网页添加一些更酷的特性。

图像切换

这里将用新的 DOM API 为网页添加另一张图片,并用 JavaScript 使图片在点击时进行切换。

  1. 首先,找到另一张你想要在你的页面上展示的图片,且尺寸与第一张图片尽可能相同。
  2. 将这张图片储存在你的images目录下。
  3. 将图片重命名为'firefox2.png'(去掉引号)。
  4. 打开 main.js 文件,输入下面的 JavaScript 代码。
  5. 这里首先把 <img> 元素的引用存放在 myImage 变量里。然后将这个变量的 onclick 事件与一个匿名函数绑定。每次点击图片时:
  6. 获取这张图片的 src 属性值。
  7. 用一个条件句来判断 src 的值是否等于原始图像的路径:
  1. 如果是,则将 src 的值改为第二张图片的路径,并在 <img> 内加载该图片。
  2. 如果不是(意味着它已经修改过), 则把 src 的值重新设置为原始图片的路径,即原始状态。
let myImage = document.querySelector('img');
myImage.onclick = function() {
     let mySrc = myImage.getAttribute('src');
     if(mySrc === 'images/firefox-icon.png') {
       myImage.setAttribute('src', 'images/firefox2.png');
     } else {
       myImage.setAttribute('src', 'images/firefox-icon.png');
     }
 }

下面来添加另一段代码,在用户初次进入站点时将网页的标题改成一段个性化欢迎信息(即在标题中添加用户的名字)。名字信息会由 Web Storage API 保存下来,即使用户关闭页面之后再重新打开,仍可得到之前的信息。还会添加一个选项,可以根据需要改变用户名字以更新欢迎信息。如下。

  1. 打开 index.html,在 <script> 标签前添加以下代码,将在页面底部显示一个“切换用户”字样的按钮:
<button>切换用户</button>
  1. 将以下 JavaScript 代码原封不动添加到 main.js 文件底部,将获取新按钮和标题的引用,并保存至变量中:
let myButton = document.querySelector('button');
let myHeading = document.querySelector('h1');
  1. 然后添加以下函数来设置个性化欢迎信息。(函数需要在调用后生效,下文中提供了两种对该函数的调用方式)
function setUserName() {
  let myName = prompt('请输入你的名字。');
  localStorage.setItem('name', myName);
  myHeading.textContent = 'Mozilla 酷毙了,' + myName;
}
  1. Copy to Clipboard该函数首先调用了 prompt() 函数,与 alert() 类似会弹出一个对话框。但是这里需要用户输入数据,并在确定后将数据存储在 myName 变量里。接下来将调用 localStorage API,它可以将数据存储在浏览器中供后续获取。这里用 localStorage 的 setItem() 函数来创建一个'name' 数据项,并把 myName 变量复制给它。最后将 textContent 属性设置为一个欢迎字符串加上这个新设置的名字。
  2. 接下来,添加以下的 if ... else 块。我们可以称之为初始化代码,因为它在页面初次读取时进行构造工作:
if(!localStorage.getItem('name')) {
  setUserName();
} else {
  let storedName = localStorage.getItem('name');
  myHeading.textContent = 'Mozilla 酷毙了,' + storedName;
}
  1. Copy to Clipboard这里首次使用了取非运算符(逻辑非,用 ! 表示)来检测 'name' 数据是否存在。若不存在,调用 setUserName() 创建。若存在(即用户上次访问时设置过),调用 getItem() 获取保存的名字,像上文的 setUserName() 那样设置 textContent。
  2. 最后,为按钮设置 onclick 事件处理器。按钮按下时运行 setUserName() 函数。这样用户就可以通过按这个按钮来自由设置新名字了:
myButton.onclick = function() {
   setUserName();
}

第一次访问网页时,页面将询问用户名并发出一段个性化的信息。可随时点击按钮来改变用户名。告诉你一个额外的福利,因为用户名是保存在 localStorage 里的,网页关闭后也不会丢失,所以重新打开浏览器时所设置的名字信息将依然存在:)

检查

运行示例代码,弹出输入用户名的对话框,试着按下 取消 按钮。此时标题会显示为“Mozilla 酷毙了,null”。这是因为取消提示对话框后值将设置为 null,这是 JavaScript 中的一个特殊值,表示引用不存在。

也可以不输入任何名字直接按 确认,你的标题会显示为“Mozilla 酷毙了,”,原因么显而易见。

要避免这些问题,应该更新 setUserName() 来检查用户是否输入了 null 或者空名字:如果 myName 没有值或值为 null,就再次从头运行setUserName()。如果有值(上面的表达式结果不为真),就把值存储到 localStorage 并且设置标题。

function setUserName() {
   let myName = prompt('请输入你的名字。');
   if(!myName) {
     setUserName();
   } else {
     localStorage.setItem('name', myName);
     myHeading.textContent = 'Mozilla 酷毙了,' + myName;
   }
 }

JavaScript 运行次序

当浏览器执行到一段 JavaScript 代码时,通常会按从上往下的顺序执行这段代码。这意味着你需要注意代码书写的顺序。比如:

const para = document.querySelector('p');

para.addEventListener('click', updateName);

function updateName() {
  let name = prompt('输入一个新的名字:');
  para.textContent = '玩家 1:' + name;
}

这里我们选定一个文本段落(第 1 行),然后给它附上一个事件监听器(第 3 行),使得在它被点击时,updateName() 代码块(code block) (5 – 8 行)便会运行。updateName() (这类可以重复使用的代码块称为“函数”)向用户请求一个新名字,然后把这个名字插入到段落中以更新显示。

如果你互换了代码里最初两行的顺序,会导致问题。浏览器开发者控制台将返回一个错误: TypeError: para is undefined。这意味着 para 对象还不存在,所以我们不能为它增添一个事件监听器。

备注: 这是一个很常见的错误,在引用对象之前必须确保该对象已经存在。

解释代码 vs 编译代码

作为程序员,你或许听说过这两个术语:解释(interpret)和 编译(compile)。

在解释型语言中,代码自上而下运行,且实时返回运行结果。代码在由浏览器执行前,不需要将其转化为其他形式。代码将直接以文本格式(text form)被接收和处理。

相对的,编译型语言需要先将代码转化(编译)成另一种形式才能运行。比如 C/C++ 先被编译成汇编语言,然后才能由计算机运行。程序将以二进制的格式运行,这些二进制内容是由程序源代码产生的。

JavaScript 是轻量级解释型语言。浏览器接受到 JavaScript 代码,并以代码自身的文本格式运行它。技术上,几乎所有 JavaScript 转换器都运用了一种叫做即时编译(just-in-time compiling)的技术;当 JavaScript 源代码被执行时,它会被编译成二进制的格式,使代码运行速度更快。尽管如此,JavaScript 仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。

javascript在html的嵌入时的

要让脚本调用的时机符合预期,需要解决一系列的问题。这里看似简单,实际大有文章。最常见的问题就是:HTML 元素是按其在页面中出现的次序调用的,如果用 JavaScript 来管理页面上的元素(更精确的说法是使用 文档对象模型 DOM),若 JavaScript 加载于欲操作的 HTML 元素之前,则代码将出错。

“内部”示例使用了以下结构:

document.addEventListener("DOMContentLoaded", function() {
  . . .
});

这是一个事件监听器,它监听浏览器的 "DOMContentLoaded" 事件,即 HTML 文档体加载、解释完毕事件。事件触发时将调用 " . . ." 处的代码,从而避免了错误发生(事件 的概念稍后学习)。

“外部”示例中使用了 JavaScript 的一项现代技术(async “异步”属性)来解决这一问题,它告知浏览器在遇到 <script> 元素时不要中断后续 HTML 内容的加载。

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

上述情况下,脚本和 HTML 将一并加载,代码将顺利运行。

备注: “外部”示例中 async 属性可以解决调用顺序问题,因此无需使用 DOMContentLoaded 事件。而 async 只能用于外部脚本,因此不适用于“内部”示例。

解决此问题的旧方法是:把脚本元素放在文档体的底端(</body> 标签之前,与之相邻),这样脚本就可以在 HTML 解析完毕后加载了。此方案(以及上述的 DOMContentLoaded 方案)的问题是:只有在所有 HTML DOM 加载完成后才开始脚本的加载/解析过程。对于有大量 JavaScript 代码的大型网站,可能会带来显著的性能损耗。这也是 async 属性诞生的初衷。

async 和 defer

上述的脚本阻塞问题实际有两种解决方案 —— async 和 defer。我们来依次讲解。

浏览器遇到 async 脚本时不会阻塞页面渲染,而是直接下载然后运行。这样脚本的运行次序就无法控制,只是脚本不会阻止剩余页面的显示。当页面的脚本之间彼此独立,且不依赖于本页面的其他任何脚本时,async 是最理想的选择。

比如,如果你的页面要加载以下三个脚本:

<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 和 script3 之前或之后调用,如果这样,后两个脚本中依赖 jquery 的函数将产生错误,因为脚本运行时 jquery 尚未加载。

解决这一问题可使用 defer 属性,脚本将按照在页面中出现的顺序加载和运行:

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

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

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

添加 defer 属性的脚本将按照在页面中出现的顺序加载,因此第二个示例可确保 jquery.js 必定加载于 script2.js 和 script3.js 之前,同时 script2.js 必定加载于 script3.js 之前。

脚本调用策略小结:

  • 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async。
  • 如果脚本需要等待页面解析,且依赖于其他脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。

应用程序接口(Application Programming Interfaces(API))将为你的代码提供额外的超能力。

API 是已经建立好的一套代码组件,可以让开发者实现原本很难甚至无法实现的程序。就像现成的家具套件之于家居建设,用一些已经切好的木板组装一个书柜,显然比自己设计,寻找合适的木材,裁切至合适的尺寸和形状,找到正确尺寸的螺钉,再组装成书柜要简单得多。

API 通常分为两类。

浏览器 API 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作。例如:

文档对象模型 API(DOM(Document Object Model)API) 能通过创建、移除和修改 HTML,为页面动态应用新样式等手段来操作 HTML 和 CSS。比如当某个页面出现了一个弹窗,或者显示了一些新内容(像上文小 demo 中看到那样),这就是 DOM 在运行。地理位置 API(Geolocation API) 获取地理信息。这就是为什么 谷歌地图 可以找到你的位置,而且标示在地图上。画布(Canvas) 和 WebGL API 可以创建生动的 2D 和 3D 图像。人们正运用这些 web 技术制作令人惊叹的作品。参见 Chrome Experiments 以及 webglsamples。诸如 HTMLMediaElement 和 WebRTC 等 影音类 API 让你可以利用多媒体做一些非常有趣的事,比如在网页中直接播放音乐和影片,或用自己的网络摄像头获取录像,然后在其他人的电脑上展示(试用简易版 截图 demo 以理解这个概念)。

第三方 API 并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息。比如:

  • Twitter API、新浪微博 API 可以在网站上展示最新推文之类。
  • 谷歌地图 API、高德地图 API 可以在网站嵌入定制的地图等等。

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

你或许还听说过**服务器端(server-side)**和 客户端(client-side)代码这两个术语,尤其是在 web 开发时。客户端代码是在用户的电脑上运行的代码,在浏览一个网页时,它的客户端代码就会被下载,然后由浏览器来运行并展示。这就是客户端 JavaScript。

而服务器端代码在服务器上运行,接着运行结果才由浏览器下载并展示出来。流行的服务器端 web 语言包括:PHP、Python、Ruby、ASP.NET 以及...... JavaScript!JavaScript 也可用作服务器端语言,比如现在流行的 Node.js 环境,你可以在我们的 动态网页 - 服务器端编程 主题中找到更多关于服务器端 JavaScript 的知识。

动态代码 vs 静态代码

“动态”一词既适用于客户端 JavaScript,又适用于描述服务器端语言。是指通过按需生成新内容来更新 web 页面 / 应用,使得不同环境下显示不同内容。服务器端代码会在服务器上动态生成新内容,例如从数据库中提取信息。而客户端 JavaScript 则在用户端浏览器中动态生成新内容,比如说创建一个新的 HTML 表格,用从服务器请求到的数据填充,然后在网页中向用户展示这个表格。两种情况的意义略有不同,但又有所关联,且两者(服务器端和客户端)经常协同作战。

没有动态更新内容的网页叫做“静态”页面**,**所显示的内容不会改变。

javascripts调试

  1. 首先在浏览器中打开你的程序。
  2. 接下来打开浏览器开发者工具, Ctrl + Shift + I,或者菜单栏
  • Chrome:菜单 ➤ 更多工具 ➤ 开发者工具
  • Safari:Develop ➤ Show Web Inspector。如果你看不到 Develop 菜单,去到Safari ➤ Preferences ➤ Advanced,然后点击Show Develop menu in menu bar 复选框。
  • Opera. Developer ➤ Web Inspector
  •  Firefox:菜单➤ Web 开发者 ➤ 切换工具箱(译者注:此处修改为最新的 Firefox Quantum),或者工具栏中的 ➤ Web 开发者 ➤ 切换工具箱

         或者右键单击网页中的一个项目上,并从弹出的菜单中选择检查元素,该方法直接将你右击的元素的代码突出显示)。

  1. 并且切换到 JavaScript 控制台的标签页。
  2. 输入 guessField,控制台将会显示此变量包含一个 <input> 元素。同时控制台还能自动补全运行环境中对象的名字,包括你的变量!
  3. 现在输入下面的代码:
guessField.value = 2;
  1. value 属性表示当前文本区域中输入的值。在输入这条指令后,你将看到文本域区中的文本被我们修改了!
  2. 现在试试输入 guesses 然后回车。控制台会显示一个包含 <p> 元素的变量。
  3. 现在试试输入下面这一行:
guesses.value
  1. 浏览器会返回 undefined,因为段落中并不存在 value 属性。
  2. 为了改变段落中的文本内容,你需要用 textContent 属性来代替 value。试试这个:
guesses.textContent = 'Where is my paragraph?';
  1. 下面是一些有趣的东西。尝试依次输入下面几行:
guesses.style.backgroundColor = 'yellow';
guesses.style.fontSize = '200%';
guesses.style.padding = '10px';
guesses.style.boxShadow = '3px 3px 6px black';
  1. 页面上的每个元素都有一个 style 属性,它本身包含一个对象,其属性包含应用于该元素的所有内联 CSS 样式。让我们可以使用 JavaScript 在元素上动态设置新的 CSS 样式。