最近实验都堆在一块了,还有期末考试,不知道什么时候才能学完,但是现在心态好多了,大概是无所谓了。
Web APIs 学习Day6
目标: 能够利用JS操作浏览器,具备利用本地存储实现学生就业表的能力
- BOM操作
- 综合案例
文章目录
- [Web APIs 学习Day6](#Web APIs 学习Day6)
-
- JavaScript组成
- Window对象
- 定时器-延迟函数
- JavaScript执行机制
- location对象
- navigator对象
- histroy对象
- 本地存储
-
- localStorage(重点)
- sessionStorage(了解)
- [localStorage 存储复杂数据类型](#localStorage 存储复杂数据类型)
- [数组 map() 方法](#数组 map() 方法)
- [数组 join() 方法](#数组 join() 方法)
JavaScript组成
JavaScript的组成
-
ECMAScript:
- 规定了js基础语法核心知识。
- 比如:变量、分支语句、循环语句、对象等等
-
Web APIs :
- DOM 文档对象模型, 定义了一套操作HTML文档的API
- BOM 浏览器对象模型,定义了一套操作浏览器窗口的API

Window对象
BOM
BOM (Browser Object Model ) 是浏览器对象模型。
-
window对象是一个全局对象,也可以说是JavaScript中的顶级对象
-
像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
-
所有通过 var 定义在全局作用域中的变量、函数都会变成window对象的属性和方法
JavaScriptvar num = 10 console.log(window.num) // 10 -
window对象下的属性和方法调用的时候可以省略window
(
document === window.document) -
我们平时定义的普通函数也是挂名在window之下
(
window fn())

定时器-延迟函数
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout
语法: setTimeout(回调函数, 等待的毫秒数)
比如:
JavaScript
setTimeout(function () {
console.log('时间到了')
}, 2000)
// 打开页面 2 秒后 仅执行一次函数
setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window。
一般情况下,我们不需要清除延时函数,但当我们递归使用时,需要在合适的时机清除延时函数:
JavaScript
let timer = setTimeout(回调函数, 等待的毫秒数)
clearTimeout(timer)
而间歇函数 setInterval 不同,每隔一段时间就执行一次,除非手动清除,平时省略 window
注意点:
-
延时函数需要等待,所以后面的代码先执行
-
返回值是一个正整数 ,表示定时器的编号(id)
JavaScript执行机制
我们来看一个经典的面试题:请说出代码输出的结果
JavaScript
// 第一题
console.log(111)
setTimeout(function () {
console.log(222)
}, 1000)
console.log(333)
显然结果是111 333 222。那么下面的题呢
JavaScript
// 第二题
console.log(111)
setTimeout(function () {
console.log(222)
}, 0)
console.log(333)
结果也是111 333 222。为什么呢?
首先,我们需要了解,JavaScript语言的一大特点就是单线程 ,也就是说,同一时间只能做一件事。
这是因为JavaScript这门脚本语言诞生的使命所致------JavaScript是为处理页面中用户的交互以及操作DOM而诞生的。比如,我们对某个DOM元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除。
单线程,意味着所有任务都需要排队,前一个任务结束,才会执行后一个任务。只有所导致的问题是:如果JavaScript执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。比方说,假如上面的面试题中,时间改为10万毫秒,由于JavaScript是单线程的,系统会等待很长时间才继续执行下一条指令,这会导致渲染阻塞。
为了解决这个问题,利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是,JavaScript中出现了同步 和异步。
同步和异步
同步是指前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。即同一时间只允许做一件事。举个做饭的例子:我们要烧水煮饭,等水开了(10分钟后)再去切菜,炒菜。
异步指我们在做一件事时,因为这件事会花费很长时间,所以我们在做这件事的同时,还可以去处理其他的事情。比如,我们在烧水煮饭的同时,利用这10分钟去切菜、炒菜。
两者的本质区别:这条流水线上各个流程的执行顺序不同。
由此,我们引申了同步任务和异步任务两个概念。
同步任务 :都在主线程上执行,形成一个执行栈(又称调用栈,call stack)。执行栈中的代码按照书写顺序依次执行,前一个任务完成后才能执行后一个任务。常见的同步任务有声明变量、函数调用。

异步任务 :JavaScript的异步指任务不会立即返回结果,而是通过回调函数、Promise等方式在将来某个时间点处理结果。(一般都是需要花费时间的任务)
一般而言,异步任务有以下三种类型:
- 普通事件:如
click、resize等 - 资源加载:如
load、error等 - 定时器,包括
setInterval、setTimeout等
异步任务相关添加到*任务队列(又称消息队列,task queue)*中。

注:JavaScript没有堆和栈,只是借助这些概念来理解执行机制。
JavaScript的执行机制
JavaScript执行机制:
- 先执行执行栈中的同步任务
- 将异步任务放入任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。执行栈执行任务完毕后,又会去查看任务队列是否有任务,有的话就放入执行栈执行。如此循环反复,构成了JavaScript的事件循环机制(event loop)。

打个比方,执行栈就像高速路上的主车道,所有车辆按顺序行驶在主车道上,如果有车辆出现故障,需要耗时修复,那么它就会被安排停车在应急车道。只有车修好了,才能继续回到主车道行驶。
JavaScript的事件循环机制其实没有那么简单,异步任务是如何被加入任务队列中,又是如何被处理,这些都包含在循环机制中。JavaScript是单线程的,它是依靠这个事件循环机制来实现多线程。而这个机制,也是基于浏览器来进行的。后面我们会系统地学习到事件循环。

现在,相信大家应该能明白面试题的结果了吧。
location对象
location (地址) 它拆分并保存了 URL 地址的各个组成部分, 它是一个对象。我们可以在控制台查看这个对象的详细信息。
html
<body>
<script>
console.log(location) // 得到一个对象
</script>
</body>
常用的属性和方法:
| 属性/方法 | 说明 |
|---|---|
| href | 属性,获取完整的 URL 地址,赋值时用于地址的跳转 |
| search | 属性,获取地址中携带的参数,符号 ?后面部分 |
| hash | 属性,获取地址中的哈希值,符号 # 后面部分 |
| reload() | 方法,用来刷新当前页面,传入参数 true 时表示强制刷新 |
location.href
获取完整的 URL 地址,赋值时用于地址的跳转:
html
<body>
<a href=""></a>
<script>
// href属性 得到完整地址,赋值则是跳转到新地址
console.log(location.href)
location.href = 'http://www.itcast.cn' // 页面自动跳转到该页面
</script>
</body>
我们可以结合定时器的使用来体验这个属性:
html
<body>
<a href="https://www.baidu.com">支付成功,5秒后自动跳转</a>
<script>
const a = document.querySelector('a')
let seconds = 5
// 页面加载一秒后才执行定时器
let intervalId = setInterval(function () {
// 先减1 因为 5 显示过了
seconds--
a.innerHTML = `支付成功,${seconds}秒后自动跳转`
if (seconds === 0) {
// 停止定时器
clearInterval(intervalId)
location.href = 'https://www.baidu.com'
}
}, 1000)
</script>
</body>
location.search
获取地址中携带的参数,符号 ?后面部分。举个例子,我们准备一个表单:
html
<body>
<form action="">
<input type="text" name="username">
<input type="password" name="pwd">
<button>提交</button>
</form>
</body>
在页面中,当我们填写表单后提交,浏览器搜索栏会出现一行网址:

而属性search的值就是 ?后面的部分:

location.hash
大部分网页,比方说网易云音乐,在头部导航栏部分随机点击一个专栏,页面会显示专栏内容,但是实现这种效果的方式不是网页的跳转,而是通过类似于tab栏内容切换的组件化来实现。我们通常在导航栏的链接a标签里href属性里添加符号 # 来区分 各种组件。而hash属性就是获取地址中的哈希值,符号 # 后面部分。

我们可以通过一个例子来体会一下:
html
<body>
<a href="#/my">我的</a>
<a href="#/friend">关注</a>
<a href="#/download">下载</a>
</body>

可以看到,当我点击'我的'页面没有经过跳转但是进入了这个板块,我们可以通过location.hash来获取当前网页哈希值。在将来学习vue的过程中我们也会学习到。
location.reload
用来刷新当前页面,传入参数 true 时表示强制刷新。
html
<body>
<button class="reload">刷新页面</button>
<script>
// reload 方法 刷新页面
const btn = document.querySelector('.reload')
btn.addEventListener('click', function () {
location.reload() // 页面刷新
location.reload(true) // 强制页面刷新 ctrl+f5
})
</script>
</body>
navigator对象
navigator是对象,该对象下记录了浏览器自身的相关信息。

常用属性和方法:
- 通过
userAgent检测浏览器的版本及平台(直接复制粘贴即可)
javascript
// 检测 userAgent(浏览器信息)
!(function () {
const userAgent = navigator.userAgent
// 验证是否为 Android或 iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone(手机端),则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}})();
histroy对象
history (历史)是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退等。
常用的属性和方法:
| history对象方法 | 作用 |
|---|---|
| back() | 可以后退功能 |
| forward() | 前进功能 |
| go(参数) | 前进和后退功能 参数是1则前进一个页面 参数是-1则后退 |
在页面中我们看到的箭头就是这个功能:

使用场景
history对象一般在实际开发中比较少用,但是会在一些OA 办公系统中见到。

我们在代码中模拟一下:
html
<body>
<button class="back">←后退</button>
<button class="forward">前进→</button>
<script>
// 前进
const forward = document.querySelector('.forward')
forward.addEventListener('click', function () {
history.forward()
// 或:
history.go(1)
})
// 后退
const back = document.querySelector('.back')
back.addEventListener('click', function () {
history.back()
// 或:
history.go(-1)
})
</script>
</body>
本地存储
以前,我们在页面中写入的数据一刷新就丢失了。随着互联网的快速发展,基于网页的应用越来越普遍,同时也变得越来越复杂,为例满足各种各样的需求,会经常性地在本地存储大量的数据,HTML5规范提出了相关解决方案。
- 将数据存储在本地浏览器中
- 设置、读取方便,甚至页面刷新或者关闭不丢失数据
- 容量较大,
sessionStorage和localStorage约 5M 左右
常见的使用场景:todolist备忘录/计划清单,即使页面刷新数据不丢失。
localStorage(重点)
作用: 可以将数据永久存储在在本地(用户的电脑中),刷新页面和关闭页面,数据也不会丢失,除非手动删除。
特性:
- 以键值对的形式存储,并且存储的是字符串, 省略了window
- 可以多窗口(页面)共享(同一个浏览器相同域名下可以共享)
语法:有存储、获取和删除数据三个语法。

存储数据
语法:localStorage.setItem(key, value)
我们在代码中体验一下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>本地存储-localstorage</title>
</head>
<body>
<script>
// 1. 存储 localStorage.setItem('键', '值')
localStorage.setItem('uname', 'lan')
</script>
</body>
</html>
打开浏览器,你可以在应用-仓库-本地仓库中看到刚才我们存储的数据,并且即使是关闭/刷新网页,这个键值对也依旧存在不会消失。当然,你要注意的是它存储仓库是在我们本机电脑的域名下(图中红线部分)

修改数据的方法也和存储数据一样,如果该键名没有,就是存储,有,就是修改值。
要注意:本地存储只能存储字符串数据类型,即使你在存储数据时输入的是数字,最后都会转换为字符串类型。
javascript
localStorage.setItem('age', 18)
console.log(typeof localStorage.getItem('age')) // string
获取数据
语法:localStorage.getItem(key)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>本地存储-localstorage</title>
</head>
<body>
<script>
// 1. 存储 localStorage.setItem('键', 值)
localStorage.setItem('uname', 'lan')
// 2. 获取 localStorage.getItem('键') 都加引号
console.log(localStorage.getItem('uname')) // lan
</script>
</body>
</html>
删除数据
语法:localStorage.removeItem(key)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>本地存储-localstorage</title>
</head>
<body>
<script>
// 1. 存储 localStorage.setItem('键', 值)
localStorage.setItem('uname', 'lan')
// 2. 获取 localStorage.getItem('键') 都加引号
console.log(localStorage.getItem('uname')) // lan
// 3. 删除 localStorage.removeItem('键')
localStorage.removeItem('uname')
</script>
</body>
</html>
这种删除是手动删除,也有一键清除所有本地存储的数据的方法,就在应用'Application'这个板块,点击下方图标即可,但是要慎用!

sessionStorage(了解)
特性:
- 用法跟localStorage基本相同
- 以键值对的形式存储使用,同一个窗口(页面)下数据可以共享
- 区别是:当页面浏览器被关闭时,存储在 sessionStorage 的数据会被清除
- 生命周期为关闭浏览器窗口(只要关闭浏览器数据就丢失)
语法:
- 存储:
sessionStorage.setItem(key,value) - 获取:
sessionStorage.getItem(key) - 删除:
sessionStorage.removeItem(key)
localStorage 存储复杂数据类型
可是如果要在本地一个一个地存储一大帮数据,是否太麻烦,我们想到了当我们想要在本地存储直接复杂数据类型时,你会发现出现了问题。
html
<body>
<script>
// 本地存储复杂数据类型
const goods = {
name: '小米',
price: 1999
}
localStorage.setItem('goods', goods)
console.log(localStorage.getItem('goods'))
// 结果为 [good, goods] 是字符串 我们看不懂
</script>
</body>
问题:本地只能存储字符串,无法存储复杂数据类型(数组、对象)。
解决 :需要将复杂数据类型转换成 JSON字符串,在存储到本地。
语法 :JSON.stringify(复杂数据类型) 这是一个方法的调用。
我们先了解一下什么是 JSON字符串:
- 首先是1个字符串
- 属性名使用双引号引起来,不能单引号
- 属性值如果是字符串型也必须双引号
html
<body>
<script>
// 本地存储复杂数据类型
const goods = {
name: '小米',
price: 1999
}
// localStorage.setItem('goods', goods)
// console.log(localStorage.getItem('goods'))
// 结果为 [good, goods] 是字符串 我们看不懂
// 1. 把对象转换为JSON字符串 JSON.stringify
localStorage.setItem('goods', JSON.stringify(goods))
console.log(localStorage.getItem('goods'))
// 结果为 {"name":"小米","price":1999} 看起来像数组 其实依旧是字符串
console.log(typeof localStorage.getItem('goods')) // string
</script>
</body>
但这里仍然有第二个问题:
问题:因为本地存储里面取出来的是字符串,不是对象,无法直接使用
解决 :把取出来的字符串转换为对象
语法 :JSON.parse(JSON字符串)
html
<body>
<script>
// 本地存储复杂数据类型
const goods = {
name: '小米',
price: 1999
}
// 1. 把对象转换为JSON字符串 JSON.stringify
localStorage.setItem('goods', JSON.stringify(goods))
// 2. 把JSON字符串转换为对象 JSON.parse
console.log(JSON.parse(localStorage.getItem('goods')))
</script>
</body>
数组 map() 方法
使用场景:
map 可以遍历数组 处理数据,并且返回新的数组。
语法: arr.map(function (ele, index) {})
html
<body>
<script>
const arr = ['red', 'blue', 'pink']
// 数组 map方法 处理数据并且返回一个数组
const newArr = arr.map(function (ele, index) {
// ele 数组元素
// index 索引号
return ele + '颜色'
})
console.log(newArr)
// ['red颜色', 'blue颜色', 'pink颜色']
</script>
</body>
map 也称为映射。映射是个术语,指两个元素的集之间元素相互"对应"的关系。

map重点在于有返回值 ,forEach没有返回值(undefined)。
数组 join() 方法
作用:join() 方法用于把数组中的所有元素转换一个字符串。小括号里可以写相关的符号,有不同的作用。括号里写什么符号,就用什么符号来分割字符串。
语法:
html
<body>
<script>
const arr = ['red', 'blue', 'pink']
// 数组join方法 把数组转换为一个字符串
// 小括号为空则逗号分割
console.log(newArr.join()) // red颜色,blue颜色,pink颜色
// 小括号是空字符串,则元素之间没有分隔符
console.log(newArr.join('')) // red颜色blue颜色pink颜色
console.log(newArr.join('|')) // red颜色|blue颜色|pink颜色
</script>
</body>
在开发中,我们常将上述两个方法结合使用来渲染页面。我们可以使用方法map()来遍历数组,每个数组元素都通过模板字符串来渲染成html格式,返回得到新的数组。接着由join()方法将新数组的每个元素进行拼接得到一个字符串,而字符串恰好能以innerHTML()方式来写入页面,实现了复杂数据类型的渲染。