浏览器是一个javascript的运行时环境,它基于js解析器的同时,增加了许多环境的内容。我们把常见的能够用js这门语言控制的内容称为一个js的运行环境,常见的运行环境有nodejs,浏览器和小程序,一些物联网设备等。所有的运行环境都必须有一个js解释器,在解释器层面符合ECMAScript规范,定义了js本身语言层面的东西比如关键字,语法等等。

在每个环境中,也会基于js开发一些当前环境中的特性,例如nodejs中的global对象,process对象,浏览器环境中的window对象,document对象等,这些属于运行环境在js基础上的内容。require是nodejs特有的运行环境中的内容,不能在浏览器端使用。

浏览器内置对象

window

window是在浏览器中代表全局作用域,所有在全局作用域下声明的变量和内容最终都会变成window对象的属性比如:

var num = 123;console.log(window.num) //123复制代码

访问未声明的变量是,如果直接访问,会报错,而如果使用window进行访问,就像通过对象访问那样,会返回undefined。

var name = oldName;   // 报错Uncaught ReferenceError: oldName is not defined
var name2 = window.oldName; // undefined复制代码

setTimeout和setInterval

他们都可以接受两个参数,第一个参数是一个回调函数,第二个参数是等待执行的时间,在等待时间结束后,就会将回调函数放到event loop中进行执行,他们都返回一个id,传入clearTimeout 和clearInterval能够清除这次的定时操作。

var id = setTimeout(function() {	console.log('hello world')
}, 2000)clearTimeout(id);复制代码

如果此时队列中没有内容,则会丽姬执行此回调函数,如果此时队列中有内容的话,则会等待内容执行完成之后再执行此函数,(所以即使等待时间结束,也不是立刻执行这个回调函数的)

因为setInterval执行时间的不确定性,所以大部分时候,我们会使用setTimeout来模拟浏览器内置对象/事件_浏览器

使用setTimeout来模拟,每次执行完成后再将下次的事件推入事件队列中浏览器内置对象/事件_浏览器_02

Location

浏览器内置对象/事件_浏览器_03

host: 返回url的主机名和端口

hostname: 主机名

port: 端口

href: 当前的url

pathname: url的路径名 如:/editor/drafts/6908977883482587149

protocol:url协议 http还是https

serch: url的查询部分  query=string

reload:重新载入当前页面

replace:用新的页面替换当前页面 域名后面的

Document

方法:选择器

选择器是考察浏览器相关知识点的重中之重,一般会结合实际场景进行考察。

getElementById,getElementsByClassName,getElementsBytagName 等早期规范定义的API,还有新增的 querySelector querySelectorAll 等新规范增加的选择器

重点: getElementsByTagName 等返回多个 node 节点的函数返回值并不是数组,而是浏览器实现的 一种数据结构。

Object.prototype.toString.call(document.getElementsByTagName('i')) // [object HTMLCollection]复制代码

方法:创建元素

document.createElement 能够创建一个 dom 元素,在新增多个元素时,可以先在内存中拼接出所 有的 dom 元素后一次插入。

 var fruits = ['Apple', 'Orange', 'Banana', 'Melon'];var fragment = document.createDocumentFragment(); // 创建虚拟的节点对象fruits.forEach(fruit => {  const li = document.createElement('li');
  li.innerHTML = fruit;
  fragment.appendChild(li);
});document.body.appendChild(fragment);复制代码

属性

  • title: document.title 可以设置或返回当前⻚面标题

  • domain: 展示当前网站的域名

  • url: 当前网站的链接

  • anchors: 返回所有的锚点,带 name 属性的 a 标签

  • forms: 返回所有的 form 标签集合

  • images: 返回所有的 img 标签集合

  • links: 返回所有带 href 属性的 a 标签

Element

Element 元素的 nodeType 均为 1 ,大多数标签都是一个 Element 实例

属性

tagName: 返回当前元素的标签名

方法

  • getAttribute:获取当前节点属性的结果

  • setAttribute:设置当前节点属性

Text类型

Text类型包含所有纯文本内容,它不支持子节点,同时她的nodeType为3

History

History 对象包含用户(在浏览器窗口中)访问过的 URL。在 HTML 5 中,history 还与客户端路由息息 相关。

属性

length: 返回历史列表中的网址数

方法

back:加载 history 列表中的前一个 URL

forward:加载 history 列表中的下一个 URL

go: 加载 history 列表中的某个具体⻚面

pushState:替换地址栏地址,并且加入 history 列表,但并不会刷新⻚面

replaceState:替换地址栏地址,替换当前⻚面在 history 列表中的记录,并不刷新⻚面

总结:

  • 全局定义的变量均可以通过 window 来进行访问。使用 setInterval 需要注意,有可能代码并不是 以相同间隔执行。使用 alert 等 API 需要注意,JS 代码可能会被阻塞。
  • location 对象需要明确对于 URL 来说,每一个类型代表的具体值是什么。
  • document 对象主要衔接 JS 和我们的 DOM 元素。需要注意这里很多选择的结果是 array-like 的 类数组元素。以及使用 createFragment 代码片段等优化,来防止浏览器多次重排造成性能问题。
  • Element 和 Text 是两个我们常⻅且易考易用的两个 DOM 对象。熟悉常⻅的方法和 debug 方式 (console.dir)其次写代码时需要明确我们当前的方法究竟是 JS 层面的,还是环境层面的。
  • history 因为和前端路由息息相关,我们需要熟悉新增的 pushState 和 replaceState 方法。
事件

我们可以通过多种方式对 DOM 元素定义一个事件:

第一种方式,直接在 dom 元素中添加,不过这种方式一般不推荐,过分的将视图与逻辑部分的代码耦合。

<script>  function showAlert() {
    alert('hello event');
}
</scripts><p onclick="showAlert()">点击后弹出 alert </p>复制代码

第二种方式,纯 JS 解决,获取 dom 元素之后通过设置其 onclick 属性

 document.getElementsByTagName('p')[0].onclick = function() {
  alert('hello world');
}// 取消事件只需要设置 onclick 属性为 null 即可 document.getElementsByTagName('p')[0].onclick = null;复制代码
  • 优点:纯 JS 实现,视图与逻辑解耦。
  • 缺点:一个 dom 元素仅能设置一个 onclick 事件

第三种方式,纯 JS 解决,DOM2 级规范实现新的 API, addEventListener 和 removeEventListener 两个 API

 var onClickFunc = function() {
  alert('hello world');
};document.getElementsByTagName('p')[0].addEventListener('click', onClickFunc);// 取消事件,使用 removeEventListener 即可 document.getElementsByTagName('p')[0].removeEventListener('click', onClickFunc);复制代码
  • 优点:

    1、纯 JS 实现,视图与逻辑解耦;

    2、通过 addEventListener 可以对 click 设置多个事件回调函数,他们会依次触发

  • 缺点:

    removeEventListener 删除的事件函数必须与设置时保持相同的函数引用,所以设置事件时 尽量不使用匿名函数。

事件捕获及冒泡

DOM 是一个嵌套性的树形树状结构,在浏览器中的表现就是叠加在一起的,所以在浏览器中点击一个 区域,在 DOM 结构中会依次遍历多个 dom,自顶向下我们称为「事件捕获」,自下而上称为 「事件 冒泡」。

DOM2 事件规范规定,一个标准的事件流分为三个阶段。首先是自上而下的「事件捕获」状态,然后是 到达真正触发事件的元素,最后再从这个元素回到顶部的「事件冒泡」。

浏览器内置对象/事件_浏览器_04

DOM2 级事件规范新增的事件定义函数 addEventListener,就可以通过第三个参数来指定究竟是在捕 获阶段触发事件还是在冒泡阶段出发事件。第三个参数为 true 则在捕获阶段触发,第三个参数为 false 则在冒泡阶段触发。

IE 中的 attachEvent 不支持捕获或冒泡阶段的选择,仅支持在冒泡阶段触发。

事件对象

触发事件之后,浏览器会传入一个事件对象进入事件回调函数本身。

document.getElementsByTagName('p')[0].onclick = function(event) {  console.log(event);
  alert('hello event');
};document.getElementsByTagName('p')[0].addEventListener('click',function(event) {  console.log(event);
})复制代码

event 对象下的属性

  • bubbles:表明事件是否冒泡
  • cancelable:表示是否可以取消事件的默认行为
  • currentTarget:事件当前正在处理的元素
  • defaultPrevented:为 true 则代表已经调用了
  • preventDefault 函数 detail:事件细节
  • eventPhase:事件所处阶段,1 代表捕获 2 代表在事件目标 3 代表冒泡
  • type:事件类型(click 等)

event 对象下的方法

  • preventDefault:取消事件的默认行为
  • stopImmediatePropagation:取消事件的进一步捕获或冒泡,同时阻止事件处理程序调用
  • stopPropagation:取消事件的进一步捕获或冒泡

IE 对象下的 event 有些许不同,如果通过 DOM0 规范定义的事件,是通过 window 来获取 event 内 容,如果是 attachEvent 定义事件,同样也是通过传入回调函数中去。

var btn = document.getElementById('btn');// DOM0 方式定义事件 btn.onclick = function() {
  var event = window.event;
};复制代码

IE 下的 event 的属性方法

  • cancelBubble:默认为 false,设置为 true 及取消了事件冒泡
  • returnValue:默认为 true,设置 false 就会取消事件默认行为
  • srcElement:事件的目标
  • type:被触发的类型

事件委托

<ul id="ul"><p>1234</p><li>1</li><li>2</li><li>3</li><li>4</li></ul> 
document.getElementById('ul').onclick = function(event) {  var target = event.target;  if (target.nodeName.toLowerCase() === 'li') {
    alert(target.innerHTML);
  }
}复制代码

通用模型

通用的事件模型主要是为了兼容多个 DOM 等级间设置事件的区别及 IE 和主流规范的不同,同时需要兼 容 event 事件本身的内容。

var addEvent = function(element, eventType, handler) {  if (element.addEventListener) {
    element.addEventListener(eventType, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent('on' + eventType, handler);
  } else {
    element['on' + eventType] = handler;
  }
}var removeEvent = function(element, eventType, handler) {  if (element.removeEventListener) {
    element.removeEventListener(eventType, handler, false);
  } else if (element.detachEvent) {
    element.detachEvent('on' + eventType, handler);
  } else {
    element['on' + eventType] = null;
  }
}var getTarget = function(event) {	return event.target || event.srcElement;
}var preventDefault = function(event) {  if (event.preventDefault) {
    event.preventDefault();
  } else {
    event.returnValue = false;
  }
}var stopPropagation = function(event) {  if (event.stopPropation) {
    event.stopPropation();
  } else {
    event.cancelBubble = true;
  }
}复制代码