JavaScript:BOM操作
- BOM与JavaScript的关系
- window对象
- window对象的常用属性方法
- 定时器
- 间歇函数
- 延时函数
- JavaScript执行机制
- 同步
- 异步
- 事件循环
- location对象
- navigator对象
- histroy对象
- 浏览器的本地存储
- localStorage
- sessionStorage
- 复杂数据类型的存储
- JSON字符串
BOM与JavaScript的关系
JavaScript的组成:
-
ECMAScript:
- 规定了js基础语法核心知识。
- 比如:变量、分支语句、循环语句、对象等等
-
Web APIs :
- DOM 文档对象模型, 定义了一套操作HTML文档的API
- BOM 浏览器对象模型,定义了一套操作浏览器窗口的API
也就是说,BOM是JavaScript的一个分支,其语法可以操控浏览器。
window对象
BOM (Browser Object Model ) 是浏览器对象模型
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
- 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
- window对象下的属性和方法调用的时候可以省略window
解析一下上面的语句:
document
是JavaScript
中最常用的对象,因为其是文档树的根节点,其分支包含着所有HTML的元素。但是document
并不是JavaScript中最高级的对象,而是window
。window
有多高级呢?
由于window
是浏览器对象模型(BOM)的根节点,所以所有的对象都是window
的后代节点,甚至你创建的变量,也会放在window
对象里。
所以window
作为一切的根,那么就可以默认存在,于是它就省略了。就好比你在填写地址的时候,绝对不会从地球开始:地球,中国,xx省…,因为人类都是默认处于地球环境中,所以我们会把地球省略。
也许你知道,var
创建的变量是全局变量,let
与const
创建的变量是局部变量,这又是为什么?
这是因为,var
创建的对象,会被直接放在window
下面,与document
同一级别,所以在DOM树中的任何一个节点往上找,都可以找到var
定义的变量。
证明:
可以看到,我们用var
创建的变量直接处于window
对象下了,而let
和const
创建的变量没有。
window对象的常用属性方法
定时器
网页中经常会需要一种功能,比如每隔一段时间,就重复改变某些元素,比如倒计时;或者说某个操作过一段时间后,浏览器做出反应,比如广告x秒后自动关闭。
以上效果都离不开定时器的使用,JavaScript中的定时器最常用的有两种:间歇函数,延时函数。
间歇函数
间歇函数的功能是,每隔一段时间,就执行指定的代码。
语法:
setInerval(函数, 间隔时间)
第一个参数为函数,其用于承载每隔一段时间就要执行的代码;第二个参数为间隔时间,也就是每隔多久执行这个函数,间隔时间的单位是ms,其中:1s = 1000ms。
接下来我写一个倒计时案例:
const div = document.querySelector("div");
let i = 10;
div.innerHTML = i;
setInterval(() => {
i--;
div.innerHTML = i;
}, 1000)
在页面中有一个盒子,我们想在其内部实现从10开始的倒计时:
我们先创建一个从10开始的变量,然后在设置一个间歇函数,时间间隔为1000即一秒,每次执行函数i–,并将盒子的内容改为i。
效果如下:
但是以上案例有一个问题,那就是当我们的i为负值的时候,我们的间歇函数不会停止。这就需要另外一个函数clearInterval
来停止间歇函数的执行了:
语法:
clearInterval(变量名)
这里的变量名,是间歇函数的编号,那么什么是间歇函数的编号?
我们在调用间歇函数的时候,间歇函数会给出一个返回值,这个返回值是一个数字,是对这个间歇函数的编号:
示例:
在示例中,创建了三个间歇函数,并获取了它们的返回值,输出返回值到控制台发现,返回值就是简单的数字,也就是每个间歇函数的编号。clearInterval
通过这个编号得知应该关闭哪一个计时器。
那么我们再优化一下倒计时的案例:
let i = 10;
div.innerHTML = i;
const timer1 = setInterval(() => {
i--;
div.innerHTML = i;
if (i === 0)
clearInterval(timer1);
}, 1000)
当i等于0时,就停止间歇函数执行下去,最后就会停在0处。
效果如下:
最后我们的倒计时就可以在0时刻停止了。
延时函数
延时函数,就是在触发某个事件后,延时一段事件再执行。
语法:
setTimeout(函数, 等待时间)
其两个参数与间歇函数是一致的,此处就不再详解了。
案例:
为网页设置一个开屏广告,并在5秒后自动关闭:
const div = document.querySelector("div");
setTimeout(() => {
div.style.display = "none";
}, 5000)
以上代码,设置了一个延时函数,并将其第二个参数设为5000即5秒。5秒后执行函数,将div的显示模式变为不显示,这样广告就消失了。
效果:
此外,延时函数也可以依靠代码停止,即clearTimeout
:
语法:
clearTimeout(变量名)
其用法与之前的间歇函数是一致的,此处就不过多讲解这个变量名是什么意思了。
JavaScript执行机制
讲完两个计时器,大家不妨看看下面这题:
请问一下代码的输出结果是什么:
console.log(111);
setTimeout(() => {
console.log(222);
}, 1000);
console.log(333);
这题非常简单,当打开页面时,先从上到下输出111
,222
,当延时函数的1s倒计时结束再输出333
。
那么以下代码的输出结果是什么:
console.log(111);
setTimeout(() => {
console.log(222);
}, 0);
console.log(333);
依照正常逻辑,由于延时函数的倒计时为0s,相当于不存在。从上往下执行输出:111
,222
,333
。
可是实际并非如此,此代码的输出结果依然为111
,333
,222
。
这是JavaScript的执行机制导致的。
JavaScript语言的一大特点就是单线程,也就是说,同一时间只能做一件事情。
单线程就意味着,所有的任务需要排队,前一个任务结束,后一个任务才会开始执行,这就会导致一个问题:当前面的代码执行时间过长,迟迟没有响应,后面的任务被阻塞,就会使用户产生页面卡顿的感觉。
为了解决这个问题,利用多核CPU的计算能力,HTML5提出了Web Worker标准,允许JavaScript脚本创建多个线程,于是JavaScript出现了同步和异步。
同步
同步是指,当前一个任务结束,才执行后一个任务,程序执行的顺序是一致的,同步的
同步任务会在执行栈
中按照顺序执行。
异步
异步是指,在执行某个任务的同时,程序还可以执行其他的任务
异步任务会在任务队列(消息队列)
中等待,等到可以执行时,插入执行栈的末尾。
一般而言,异步任务有以下类型:
普通事件,如
cilck
,resize
资源加载,如load
,error
定时器,如setInterval
,setTimeout
事件循环
在同步与异步之间,存在着一个事件事件循环的交互关系,即:
当执行栈中的所有同步任务执行完毕,系统会每隔一段时间读取任务队列中的异步任务,如果异步任务可以执行了,就会结束等待状态,从任务队列进入到执行栈中,开始执行。
事件循环可以拆分为以下过程:
1.先执行执行栈中的同步任务
2.将异步任务放到任务队列中
3.执行栈执行完所有同步任务
4.每隔一段时间读取任务队列中的任务
5.如果任务队列中有可执行的任务,就放到执行栈中
6.回到步骤1,再次循环
由于这个过程是从1-6循环执行,所以这个过程也叫做事件循环。
我们看到一开始的问题,来帮助理解:
console.log(111);
setTimeout(() => {
console.log(222);
}, 0);
console.log(333);
在这串代码中,有三个输出语句,其中111
和333
是一般的语句,会放入执行栈中,而222
语句被放在了定时器中,虽然定时器时间为0,但是不会立即执行,而是进入任务队列中等待。
当任务被安排好:
执行执行栈中的任务:输出
111
和333
读取任务队列中的任务console.log(222)
到执行栈中
执行执行栈中的任务:输出222
所以这题是JavaScript的执行机制导致的问题,虽然console.log(222)
在console.log(333)
的前面,但是由于其是定时器的特性,会进入任务队列,导致其执行顺序会晚于console.log(333)
。
location对象
location对象保存了当前页面的URL地址,并且可以根据方法来拆分不同的部分。
常用属性和方法:
属性/方法 | 说明 |
---|---|
href | 属性,获取完整的 URL 地址,赋值时用于地址的跳转 |
search | 属性,获取地址中携带的参数,符号 ?后面部分 |
hash | 属性,获取地址中的啥希值,符号 # 后面部分 |
reload() | 方法,用来刷新当前页面,传入参数 true 时表示强制刷新 |
三个属性:
以CSDN主页为例,我们通过三个属性,分别输出了这个链接的不同部分,由于此链接没有哈希值,所以hash输出为空字符串。
此外,href属性是一个可读写属性,可以在JavaScript中修改当前得到href属性,实现页面的跳转。
reload()方法,则是一个强制刷新页面的方法,调用后,传入true参数,当前页面就会强制刷新。
navigator对象
navigator对象,其记录下了当前浏览器的相关信息,当某些浏览器不兼容你的网页时,可以通过这个对象的属性值来判断。
常用属性和方法:
- 通过 userAgent 检测浏览器的版本及平台:
// 检测 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对象其内部存储着浏览器的历史记录,并且可以实现页面的前进后退功能。
常见方法:
方法 | 作用 |
---|---|
back() | 后退 |
forward() | 前进 |
go(参数) | 可以前进和后退。如果参数为1就前进一个页面,如果为-1就后退一个页面 |
这个对象的使用已经很少了,因为大部分浏览器自带前进后退功能。
浏览器的本地存储
随着互联网的发展,网页的应用越来越广泛,也变得越来越复杂,为了满足需求,HTML5提供了本地存储的方案,来在本地存储大量数据。
通过浏览器本地存储的数据有以下特性:
数据存储在用户浏览器中
设置,读取方便,可以做到刷新页面也不丢失数据
容量约为5m,满足绝大部分需求
本地存储分为两类,分别是可以永久存储在本地的 localStorage
和在当前浏览器临时使用的sessionStorage
。
localStorage
localstorage
可以将数据永久存储正在本地(用户的电脑),除非手动删除,否则关闭网页也会存在。
特性:
可以多窗口共享(同一浏览器内)
语法:
方法 | 作用 |
---|---|
setItem(key, value) | 存储/修改数据 |
getItem(key) | 读取数据 |
removeItem(key) | 删除数据 |
数据的增删查改是以键值对的形式,key为键,可以理解为数据的名称;value为值,即对应数据的值。
比如存储一个张三的年龄:localStorage.setItem("张三", 18)
。
在浏览器中,我们也可以查看存储的数据:
通过localStorage
存储的数据会永久存储在本地,就算关闭浏览器也依然会存在:
我们修改代码后,直接访问张三
试试:
可以看到,这一次我们的代码中没有输入语句,但是我们可以直接访问张三的年龄了,这是因为上一次已经将数据存在用户的电脑中了,不论何时在浏览器打开,都可以直接访问这个值。
sessionStorage
sessionStorage
会将数据临时存储下来,当用户关闭浏览器,数据就会被自动删除。
特性:
生命周期为关闭浏览器窗口
同一个窗口下的数据可以共享
其语法和localStorage
是完全一致的,此处不再做详解了。
语法:
方法 | 作用 |
---|---|
setItem(key, value) | 存储/修改数据 |
getItem(key) | 读取数据 |
removeItem(key) | 删除数据 |
复杂数据类型的存储
先前讲过两种存储数据的方法,我们现在利用这个存储方式尝试存储一个对象:
最后我们没有输出对象的值,而是输出了[object object]
,也就是说对象这种复杂数据类型,不能直接存储。
为了可以存储复杂数据类型,JavaScript推出了JSON字符串,其是一种专门用来存储对象的字符串。
JSON字符串
将对象转化为JSON字符串的语法:
JSON.stringify(对象)
现在转化一个对象为JSON字符串,看看JSON字符串是一个什么样的形式:
可以看到,JSON字符串其实就是将所有的属性放在了同一行,并转化为了键值对的形式,其会为每一个属性名添加双引号,比如name
变成了“name”
,除此之外,和我们的基本语法别无二致。
被转化为字符串后,字符串就可以存储下来了。
那么我们要如何取用这个数据呢?
JSON的另外一个方法可以将JSON字符串转化为对象
语法:
JSON.parse(JSON字符串)
我们通过这个语法来取用数据:
可以看到,我们直接取用,得到的是一个字符串,而通过JSON.parse(JSON字符串)后,我们最后获得的就是一个对象了。