Web APIs 学习第六天:BOM、location对象与本地存储

最近实验都堆在一块了,还有期末考试,不知道什么时候才能学完,但是现在心态好多了,大概是无所谓了。

Web APIs 学习Day6

目标: 能够利用JS操作浏览器,具备利用本地存储实现学生就业表的能力

  • BOM操作
  • 综合案例

文章目录

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对象的属性和方法

    JavaScript 复制代码
    var 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等方式在将来某个时间点处理结果。(一般都是需要花费时间的任务)

一般而言,异步任务有以下三种类型:

  • 普通事件:如clickresize
  • 资源加载:如loaderror
  • 定时器,包括setIntervalsetTimeout

异步任务相关添加到*任务队列(又称消息队列,task queue)*中。

注:JavaScript没有堆和栈,只是借助这些概念来理解执行机制。

JavaScript的执行机制

JavaScript执行机制:

  1. 先执行执行栈中的同步任务
  2. 将异步任务放入任务队列中
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。执行栈执行任务完毕后,又会去查看任务队列是否有任务,有的话就放入执行栈执行。如此循环反复,构成了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是对象,该对象下记录了浏览器自身的相关信息

常用属性和方法:

  • 通过 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规范提出了相关解决方案。

  1. 将数据存储在本地浏览器中
  2. 设置、读取方便,甚至页面刷新或者关闭不丢失数据
  3. 容量较大,sessionStoragelocalStorage 约 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 的数据会被清除
  • 生命周期为关闭浏览器窗口(只要关闭浏览器数据就丢失)

语法:

  1. 存储:sessionStorage.setItem(key,value)
  2. 获取:sessionStorage.getItem(key)
  3. 删除: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()方式来写入页面,实现了复杂数据类型的渲染。

相关推荐
LIZhang20162 小时前
基于ffmpeg8.0录制mp4文件
开发语言·c++
百***92652 小时前
Node.js npm 安装过程中 EBUSY 错误的分析与解决方案
前端·npm·node.js
juejin_cn2 小时前
JavaScript 对象数组去重的几种方法
javascript
_OP_CHEN2 小时前
C++进阶:(九)深度剖析unordered_map 与 unordered_set容器
开发语言·c++·stl容器·哈希表·哈希桶·unordered_map·unordered_set
七夜zippoe2 小时前
Java并发编程基石:深入理解JMM(Java内存模型)与Happens-Before规则
java·开发语言·spring·jmm·happens-before
程序员小寒2 小时前
前端高频面试题之Vue(高级篇)
前端·javascript·vue.js
Mark Studio2 小时前
QT linux 静态编译问题记录
开发语言·qt
呜呜。2 小时前
WebSocket-学习调研
websocket·网络协议·学习
m0_639817152 小时前
基于springboot纺织品企业财务管理系统【带源码和文档】
java·服务器·前端