前端面试复习5---浏览器内置对象

浏览器内置对象

一、BOM 和 DOM

JavaScript 在浏览器环境下一般有三部分组成:ECMAScript、BOM、DOM,(IE 浏览器多了 ActiveXObject 类)。

浏览器环境是 JavaScript 的宿主环境,浏览器中的编译引擎:V8, JSCore, SpiderMoney

  1. ECMAScript:实现 JavaScript 的标准,它的核心描述了 js 的语法和基本对象,我们常说的 ES6 ,就是属于这个部分的内容。
  2. BOM:Browser Object Model,浏览器对象模型。BOM 顾名思义其实就是为了控制浏览器的行为而出现的接口。
    1. 浏览器的本质,就是接管了你开发的软件中,负责渲染 GUI 的部分。
    2. BOM 是浏览器中除了代码展示部分。

题外话:W3C、ECMAScript 以及 MDN 这些关系是什么?

W3C

  1. W3C 是一个非标准化的组织,最重要的工作是发展 Web 规范。
  2. 这些规范描述了 Web 的通信协议(比如 HTML 和 XHTML)和其他的构建模块。
  3. 平时我们接触到的标准,比如:超文本标记语言、HTML5 规范、事件规范,我们平时接触到 DOM 一级、二级规范都是该组织制定的。
  4. 标准制定后,就需要各方支持实现。得到了浏览器厂商的支持,所以会有一致的表现。

ECMAScript

  1. ECMAScript 是一种标准,这个标准主要用于标准化 JavaScript 这种语言的,比如我们平时使用的 es6、es7 等都是该标准的产物。该标准由 ECMA International 进行制定,TC39 委员会进行监督。
  2. 指定的标准有以下等:
    1. 语法:关键词,解析规则,流程控制,对象初始化,等等...
    2. 类型:数字,字符串,布尔值,对象,函数,等等...
    3. 全局对象:在浏览器环境中,这个全局对象就是 window 对象,但是 ECMAScript 只定义那些不特定于浏览器的 API(例如:parseIntparseFloatdecodeURIencodeURI,等等)
    4. 内置对象和函数:JSONMathArray.prototype方法、对象内省(自检,自我检查,introspection)方法,等等...
    5. 错误处理机制:throwtry...catch,以及创建用户定义错误类型的能力
    6. 基于原型的继承机制
    7. Strict mode

MDN

  1. MDN:全称 Mozilla Developer Network。
  2. 它和前面的 W3C 和 ECMAscript 不太一样,这个组织不是为了标准化而诞生的。在 MDN 的官网上的左上⻆写着 MDN web docs,很明显这是一个专为开发者服务的开发文档。
  3. 当然,W3C 和 ECMAScript 也有对应的文档,但是平时开发的过程中,大家都比较习惯用 MDN 去查询资料,主要是 MDN 做的也太好了,有各种比较容易理解的使用说明和兼容性说明等。

MSDN

  1. 全称为 microsoft developer network,开发在 IE 浏览器上运行的程序需要参考的文档。
  2. 鉴于平时 IE 支持到 11 后,很多属性都已经标准化了,所以平时用的比较少。

二、BOM

属性 说明
appName 返回浏览器的名称--Netscape
appCodeName 返回浏览器的代码名
appVersion 返回浏览器的平台和版本信息
cookieEnabled 返回指明浏览器中是否启用 cookie 的布 尔值
platform 返回运行浏览器的操作系统平台,可以用于判断快捷键,例如:ctrl+s / command+s
userAgent 返回由客户机发送服务器的 user-agent 头部的值
onLine 判断浏览器是否在线
connection 自动检测网络状况切换清晰度

onLine 和 connection 的 demo

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <div id="status"></div>
    <div id="desc"></div>
    <div id="condition"></div>
  </body>

  <script>
    window.addEventListener('load', () => {
      const statusDiv = document.getElementById('status')
      const descDiv = document.getElementById('desc')
      const conditionDiv = document.getElementById('condition')

      // 监听离线和在线事件
      window.addEventListener('online', handleLineChange)
      window.addEventListener('offline', handleLineChange)

      function handleLineChange(event) {
        const status = navigator.onLine ? 'online' : 'offline'
        statusDiv.innerHTML = status
        descDiv.innerHTML = `Event: ${event.type}; Status:${status}`
      }

      const connection = navigator.connection
      let type = connection.effectiveType
      function updateConnectionCondition() {
        conditionDiv.innerHTML = `Network Contidition transferred from ${type} to ${connection.effectiveType}`
        type = connection.effectiveType
      }
      // 当网络状态发生变化
      connection.addEventListener('change', updateConnectionCondition)
    })
  </script>
</html>

2. location

属性 描述 栗子
hash 锚点,简单来说就是 url 的#后面所用内容 #detail
host 主机名+端口 www.baidu.com:8080
hostname 主机名 <www.baidu.com>
href 完整路径 <www.baidu.com?id=1#detail>
pathname host 后面,hash # 号前面的路径部分 /index.html
port 端口 8080
protocol 协议 http: , https:
search 参数,? 之后的部分,注意不能放在 hash 的后面,否则读取不到 ?id=1
origin 协议+主机名+端口 只有它是只读的,其余均可写
方法 描述
assign 加载新的文档
reload 重新刷新⻚面,相当于刷新按钮
replace 用新的文档替换当前文档,移动设备检测时的立刻跳转

3. screen

属性 说明
availHeight 返回屏幕的高度(不包括 Windows 任务栏)
availWidth 返回屏幕的宽度(不包括 Windows 任务栏)
colorDepth 返回目标设备或缓冲器上的调色板的比特深度
height 返回屏幕的总高度
pixelDepth 返回屏幕的颜色分辨率(每象素的位数)
width 返回屏幕的总宽度

浏览器的各种宽度、高度总结

  1. window.innerWidth 和 window.innerHeight :文档显示区的宽度/高度包括滚动条,只读属性,无默认值。IE 8 及更早 IE 版本不支持这两个属性,可以使用 clientWidth 和 clientHeight 属性。
  2. window.outerWidth 和 window.outerHeight :窗口的外部高度,包括所有界面元素(如:tab 标签栏/收藏夹栏、工具栏/滚动条),只读属性,无默认值。
  3. Element.clientWidth 和 Element.clientHeight : width/height + padding,只读属性,内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。
  4. Element.offsetWidth 和 Element.offsetHeight : width/height + padding + border,包含滚动条
  5. Element.scrollWidth 和 Element.scrollHeight : width/height + padding + 视口之外的宽度/高度

4. history

属性/方法 说明
length 返回历史列表中的网址数
back() 加载 history 列表中的前一个 URL
forward() 加载 history 列表中的下一个 URL
go() 加载 history 列表中的某个具体页面
pushState() HTML5 新增的方法,不会触发 onpopstate
replaceState()) HTML5 新增的方法,不会触发 onpopstate

HTML5 为 history 对象添加了两个新方法,history.pushState()history.replaceState(),用来在浏览历史中添加和修改记录。

js 复制代码
if (!!(window.history && history.pushState)) {
  // 支持 pushState
} else {
  // 不支持
}

history.pusuState() 接收三个参数

  • state:一个与指定网址相关的状态对象,popstate 事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填 null, 设置 state 后会表现在 history.state

  • title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null

  • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

    假定当前网址是 example.com/1.html,我们使用 pushState方法在浏览记录(history对象)中添加一个新记录。

    js 复制代码
    var stateObj = { foo: 'bar' }
    history.pushState(stateObj, 'page 2', '2.html')

    添加上面这个新记录后,浏览器地址栏立刻显示 example.com/2.html,但并不会跳转到 2.html,甚至也不会检查 2.html是否存在,它只是成为浏览历史中的最新记录。这时,你在地址栏输入一个新的地址(比如访问 google.com),然后点击了倒退按钮,页面的 URL 将显示 2.html;你再点击一次倒退按钮,URL 将显示 1.html

    总之,pushState方法不会触发页面刷新,只是导致 history对象发生变化,地址栏会有反应。

    如果 pushState的 url 参数,设置了一个新的锚点值(即 hash),并不会触发 hashchange事件。如果设置了一个跨域网址,则会报错。

    js 复制代码
    // 报错
    history.pushState(null, null, 'https://twitter.com/hello')

    上面代码中,pushState 想要插入一个跨域的网址,导致报错。这样设计的目的是,防止恶意代码让用户以为他们是在另一个网站上。

history.replaceState()

replaceState 方法的参数与 pushState 方法一模一样,区别是它修改浏览历史中当前纪录。即 history.length 的长度不会发生变化

假定当前网页是 example.com/example.html

js 复制代码
history.pushState({ page: 1 }, 'title 1', '?page=1')
history.pushState({ page: 2 }, 'title 2', '?page=2')
history.replaceState({ page: 3 }, 'title 3', '?page=3')

history.back()
// url显示为http://example.com/example.html?page=1

history.back()
// url显示为http://example.com/example.html

history.go(2)
// url显示为http://example.com/example.html?page=3

popstate 事件

每当同一个文档的浏览历史(即 history对象)出现变化时,就会触发 popstate事件。

需要注意的是,仅仅调用 pushState方法或 replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用 backforwardgo方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

使用的时候,可以为 popstate事件指定回调函数。这个回调函数的参数是一个 event事件对象,它的 state属性指向 pushStatereplaceState方法为当前 URL 所提供的状态对象(即这两个方法的第一个参数)。

js 复制代码
window.onpopstate = function (event) {
  console.log('location: ' + document.location)
  console.log('state: ' + JSON.stringify(event.state))
}

// 或者

window.addEventListener('popstate', function (event) {
  console.log('location: ' + document.location)
  console.log('state: ' + JSON.stringify(event.state))
})

上面代码中的 event.state,就是通过 pushStatereplaceState方法,为当前 URL 绑定的 state对象。

这个 state对象也可以直接通过 history对象读取。

js 复制代码
var currentState = history.state

注意,页面第一次加载的时候,在 load事件发生后,Chrome 和 Safari 浏览器(Webkit 核心)会触发 popstate事件,而 Firefox 和 IE 浏览器不会。

5. Window 对象属性/方法

属性 描述
open() 打开一个新的浏览器窗口或查找一个已命名的窗口。
opener 返回对创建此窗口的窗口的引用。
focus() 把键盘焦点给予一个窗口。
close() 关闭浏览器窗口。
closed 返回窗口是否已被关闭。
parent 返回父窗口,与 opener 类似
print() 打印当前窗口的内容。
postMessage() 安全地实现跨源通信。
encodeURI 会将元字符和语义字符外的全转码。encodeURI('http://www.baidu.com/query=百度') 转成 http://www.baidu.com/query=%E7%99%BE%E5%BA%A6
encodeURIComponent 会将语义字符以外的全部转码,元字符也会转。encodeURIComponent('http://www.baidu.com/query=百度') 转成 http%3A%2F%2Fwww.baidu.com%2Fquery%3D%E7%99%BE%E5%BA%A6
devicePixelRatio 物理、逻辑像素比。一般的显示器 72 像素/英寸。

open(), opener, close(), closed, focus() 应用 demo

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>open(),opener,close(),closed</title>
    <script>
      function openNewWindown() {
        newWindown = window.open()
        newWindown.document.write('<p>这是新的窗口!</p>')
        newWindown.focus()
        newWindown.opener.document.body.style.background = 'pink'
      }

      function closeNewWindown() {
        alert(newWindown.closed)
        newWindown.close()
        alert(newWindown.closed)
      }
    </script>
  </head>

  <body>
    <input type="button" value="打开一个新的窗口" onclick="openNewWindown()" />
    <input type="button" value="关闭新打开的窗口" onclick="closeNewWindown()" />
  </body>
</html>

三、DOM

1. 事件级别

  • DOM 级别一共可以分为四个级别:DOM0 级、DOM1 级、DOM2 级、DOM3 级。
  • 而 DOM 事件分为 3 个级别:DOM0 级事件处理,DOM2 级事件处理和 DOM3 级事件处理。
  • 由于 DOM1 级中没有事件的相关内容,所以没有 DOM1 级事件。

DOM0

DOM0 不是 W3C 规范,而仅仅是对在 NetscapeNavigator 3.0 和 MicrosoftInternetExplorer 3.0 中的等价功能性的一种定义。

js 复制代码
const btn = document.getElementById('btn')
btn.onclick = function () {
  alert(this.innerHTML)
}
  • 当希望为同一个元素/标签绑定多个同类型事件的时候(如给上面的这个 btn 元素绑定 3 个点击事件),是不被允许的。
  • DOM0 事件绑定,给元素的事件行为绑定方法,这些方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的。

DOM1

DOM1 专注于 HTML 和 XML 文档模型。它含有文档导航和处理功能。

DOM2

  • DOM2 对 DOM1 添加了样式表对象模型,并定义了操作附于文档之上的样式信息的功能性。
  • DOM2 同时还定义了一个事件模型(Events,规定了访问文档事件的 API),并提供了对 XML 命名空间的支持。
js 复制代码
const btn = document.getElementById('btn')
btn.addEventListener('click', test, false)
function test(e) {
  e = e || window.event
  alert((e.target || e.srcElement).innerHTML)
  btn.removeEventListener('click', test)
}

// IE9-: attachEvent(), detachEvent()
// IE9+/chrom/FF: addEventListener(), removeEventListener()

DOM3

  • DOM3 规定了内容模型(DTD 和 Schemas)和文档验证。
  • 同时规定了文档加载和保存、文档查看、文档格式化和关键事件。
  • 在 DOM 2 级事件的基础上添加了更多的事件类型。
    • UI 事件,当用户与页面上的元素交互时触发,如:load、scroll
    • 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
    • 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dblclick、mouseup
    • 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
    • 文本事件,当在文档中输入文本时触发,如:textInput
    • 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
    • 合成事件,当为 IME(输入法编辑器)输入字符时触发,如:compositionstart 变动事件,当底层 DOM 结构发生变化时触发,如:DOMsubtreeModified
    • 同时 DOM3 级事件也允许使用者自定义一些事件。

HTML DOM 常用事件

2. DOM 事件模型和事件流

  • DOM 事件模型分为捕获冒泡
  • 一个事件发生后,会在子元素和父元素之间传播。这种传播分成三个阶段:
    1. 捕获阶段:事件从 window 对象自上而下向目标节点传播的阶段;
    2. 目标阶段:真正的目标节点正在处理事件的阶段;
    3. 冒泡阶段:事件从目标节点自下而上向 window 对象传播的阶段。

事件捕获具体流程,事件冒泡流程正好相反

graph LR window-->document-->html-->body-->目标元素
  1. addEventListener 的第三个参数,如果为 true,就是代表在捕获阶段执行。如果为 false,就是在冒泡阶段进行,默认为 false 。
  2. e.preventDefault() 阻止默认事件,例如阻止 a 标签链接的跳转。IE 用 e.returnValue = false
  3. e.stopPropagation() 阻止事件捕获或者冒泡。IE 用 e.cancelBubble = true 只能阻止冒泡
  4. e.stopImmediatePropagation() 不仅会阻止捕获或者冒泡,而且对于多个相同类型的事件的事件监听函数绑定同一个元素的情况,会在触发时阻止该元素的其他事件。
html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #parent {
      width: 800px;
      height: 800px;
      background-color: antiquewhite;
    }

    #child {
      width: 400px;
      height: 400px;
      background-color: aquamarine;
    }

    #son {
      width: 200px;
      height: 200px;
      background-color: rgb(121, 216, 11);
    }

    .flex-center {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      font-size: large;
    }
  </style>
</head>

<body>
  <div id="parent" class="flex-center">
    parent
    <div id="child" class="flex-center">
      child
      <div id="son" class="flex-center">
        son
        <a id="link" href="https://www.baidu.com/">www.baidu.com</a>
      </div>
    </div>

    <script>
      const parent = document.getElementById('parent');
      const child = document.getElementById('child');
      const son = document.getElementById('son');

      window.addEventListener('click', (e) => {
        console.log('window 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      document.addEventListener('click', (e) => {
        console.log('document 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      document.querySelector('html').addEventListener('click', (e) => {
        console.log('html 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      document.body.addEventListener('click', (e) => {
        console.log('body 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      parent.addEventListener('click', (e) => {
        console.log('parent 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      child.addEventListener('click', (e) => {
        console.log('child 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      son.addEventListener('click', (e) => {
        // e.stopPropagation() 可以阻止向下捕获
        console.log('son 捕获---', e.target.id, e.currentTarget.id)
      }, true)
      link.addEventListener('click', (e) => {
        console.log('link 捕获---', e.target.id, e.currentTarget.id)
        console.log('--------------------------------------------')
      }, true)

      window.addEventListener('click', (e) => {
        console.log('window 冒泡---', e.target.id, e.currentTarget.id)
      })
      document.addEventListener('click', (e) => {
        console.log('document 冒泡---', e.target.id, e.currentTarget.id)
      })
      document.querySelector('html').addEventListener('click', (e) => {
        console.log('html 冒泡---', e.target.id, e.currentTarget.id)
      })
      document.body.addEventListener('click', (e) => {
        console.log('body 冒泡---', e.target.id, e.currentTarget.id)
      })
      parent.addEventListener('click', (e) => {
        console.log('parent 冒泡---', e.target.id, e.currentTarget.id)
      })
      child.addEventListener('click', (e) => {
        console.log('child 冒泡---', e.target.id, e.currentTarget.id)
      })
      son.addEventListener('click', (e) => {
        console.log('son 冒泡---', e.target.id, e.currentTarget.id)
      })
      link.addEventListener('click', (e) => {
        e.preventDefault();
        console.log('e.preventDefault() 阻止了a标签的链接跳转')

        e.stopPropagation();
        console.log('e.stopPropagation() 阻止了a标签的事件冒泡')

        e.stopImmediatePropagation()
        console.log('e.stopImmediatePropagation() 不单单可以阻止事件冒泡,还可以阻止其他的绑定事件')

        console.log('link 冒泡---', e.target.id, e.currentTarget.id)
      })
      link.addEventListener('click', (e) => {
        console.log('这里被 e.stopImmediatePropagation() 阻止了,不会被打印')
      })
    </script>
</body>

</html>

3. 事件委托

js 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>

  <script>
    const ul = document.querySelector('ul')
    ul.addEventListener('click', function (e) {
      const { target } = e
      if (target.tagName.toLowerCase() === 'li') {
        const liList = this.querySelectorAll('li')
        let index = Array.prototype.indexOf.call(liList, target)
        alert(index)
      }
    })
  </script>
</body>

</html>
相关推荐
真的很上进4 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
噢,我明白了8 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__8 小时前
APIs-day2
javascript·css·css3
关你西红柿子8 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根9 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.9 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Aphasia3119 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
m0_748256569 小时前
Vue - axios的使用
前端·javascript·vue.js
m0_7482563410 小时前
QWebChannel实现与JS的交互
java·javascript·交互
胡西风_foxww10 小时前
【es6复习笔记】函数参数的默认值(6)
javascript·笔记·es6·参数·函数·默认值