浏览器是一个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来模拟,每次执行完成后再将下次的事件推入事件队列中
Location
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 元素的 nodeType 均为 1 ,大多数标签都是一个 Element 实例
属性
tagName: 返回当前元素的标签名
方法
-
getAttribute:获取当前节点属性的结果
-
setAttribute:设置当前节点属性
Text类型包含所有纯文本内容,它不支持子节点,同时她的nodeType为3
HistoryHistory 对象包含用户(在浏览器窗口中)访问过的 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 事件规范规定,一个标准的事件流分为三个阶段。首先是自上而下的「事件捕获」状态,然后是 到达真正触发事件的元素,最后再从这个元素回到顶部的「事件冒泡」。
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; } }复制代码