浏览器是一个JS的运行时环境,它基于JS解析器的同时,增加了许多环境相关的内容,用一张图表示各个运行环境和JS解析器的关系如下:
我们把常见的,能够用JS这门语言控制的内容称为一个JS的运行环境,常见的运行环境又Nodejs,浏览器,小程序,一些物联网设备等等,所有运行环境都必须有一个JS的解释器,在解释器层面符合ECMAScript规范,定义了JS本身语言层面的东西如关键字,语法等等。
在每个环境中,也会基于JS开发一些当前环境的特性,例如Nodejs中的global对象,process对象,浏览器环境中的window对象,document对象等等,这些属性属于运行环境在JS基础上的内容。
这也就解释了为什么在Nodejs和浏览器中都能使用数组、函数,但是只能在nodejs中使用require加载模块,而不能在浏览器端使用,因为require是nodejs特有的运行环境中的内容。
内置对象属性
window
window是在浏览器中代表全局作用域,所有在全局作用域下声明的变量和内容最终都会变成window对象下的属性。例:
var num = 123
console.log(window.num) // 123
访问未声明的变量时,如果直接访问则会报错,而如果使用window进行访问,就像通过对象访问那样,会返回undefined
var name = oldName // 报错
var name2 = window.oldName // undefined
setTimeout 和 setInterval
setTimeout 和 setInterval他们都可以接受两个参数,第一个参数是回调函数,第二个参数是等待执行的时间,在等待时间结束之后,就会将回调函数放到event loop中进行执行,他们都返回一个id,传入clearTimeout和clearInterval都能够清除这次的定时操作。
var id = setTimeout(function () {
console.log("hello")
}, 1000)
clearTimeout(id)
可视化工具网站: http://latentflip.com/loupe
alert,confirm,prompt 等交互api
这里使用了这类API后,会导致页面JS停止执行
Location
属性:
- hash:返回URL的锚部分
- host:返回一个URL的主机名和端口
- hostname:返回一个URL的主机名
- href:当前url
- pathname:返回URL的路径名
- port:返回URL服务器使用的端口号
- protocol:返回一个URL的协议
- search:返回URL的查询部分
方法:
- reload:重新载入当前页面
- replace:用新的页面替换当前页面
Document
方法:选择器
getElementById、getElementByClassName等API
方法:创建元素
createElement
属性:
- title:可以设置或返回当前页面标题
- domain:展示当前网站的域名
- url:当前网站的链接
- anchors:返回所有锚点,带name属性的a标签
- forms:返回所有form标签集合
- images:返回所有img标签集合
- links:返回所有带href属性的a标签
Element
Element元素的nodeType均为1,大多数变迁都是一个Element实例
属性:
tagName:返回当前元素的标签名
方法:
setAttribute:获取当前节点属性的结果
setAttrbute:设置当前节点属性
Text类型
Text类型包括所有村文本内容,它不支持子节点,同时他的nodeType为3
History
History对象包含用户访问过的URL,在HTML5中,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方式
- history因为和路由息息相关,我们需要熟悉新增的pushState和replaceState方法
事件
定义事件
我们可以通过多种方式对DOM元素定义一个事件
<p>点击后弹出alert</p>
第一种方式直接在dom元素中添加,这种方式不推荐
<p onclick="showAlert()">点击后弹出alert</p>
<script>
function showAlert() {
alert('hello')
}
</script>
第二种方式,纯JS解决,获取dom元素之后通过设置其onclick属性
document.getElementByTagName('p')[0].onclick = function () {
alert('hello')
}
// 取消事件只需要设置onclick属性为null即可
document.getElementByTagName('p')[0].onclick = null
优点:纯JS实现,视图与逻辑解耦
缺点:一个dom元素仅能设置一个onclick事件
第三种方式,纯JS解决,DOM2级规范实现新的API,addEventListener和removeEventListener两个API
var onClickFunc = function () {
alert('hello')
}
document.getElementByTagName('p')[0].addEventListener('click', onClickFunc)
// 取消事件
document.getElementByTagName('p')[0].removeEventListener('click', onClickFunc)
优点:纯JS实现,视图与逻辑解耦,可以设置多个回调函数
缺点:removeEventListener删除事件函数必须与设置时保持相同的函数引用,所以设置事件尽量不要用匿名函数
事件捕获及冒泡
DOM是一个嵌套性的树形树状结构,在浏览器中的表现就是叠加在一起的,所以在浏览器中点击一个区域,在DOM结构中会一次遍历多个dom,自顶向下称为【事件捕获】自下而上称为【事件冒泡】
DOM2事件规范规定,一个标准事件流氛围三个阶段,首先手自上而下的【事件捕获】状态,然后是真正触发事件的元素,最后这个元素再回到顶部的事件冒泡
DOM2级事件规范新增的事件定义函数addEventListener就可以通过第三个参数来指定究竟是在捕获阶段触发还是在冒泡阶段触发,第三个参数为true则是在捕获阶段触发,第三个参数为false,则在冒泡阶段触发
事件对象
触发事件之后,浏览器会传入一个事件对象进入事件回调函数本身
document.getElementsByTagName('p')[0].onclick = function (event) {
console.log(event)
}
document.getElementsByTagName('p')[0].addEventListener('click', function (event) {
consolel.log(event)
})
event 对象属性如下:
- bubbles:表明事件是否冒泡
- cancelable:表示是否可以取消事件的默认行为
- currentTarget:事件当前正在处理的元素
- defaultPrevented: 为true则代表已经调用了preventDefault函数
- detail:事件细节
- eventPhase:事件所处阶段,1代表捕获,2代表在事件目标,3代表冒泡
- type:事件类型(click等)
event对象方法如下:
- preventDefault:取消事件的默认行为
- stopImmediatePropagation:取消事件的进一步捕获或冒泡,同时组织事件处理程序调用
- stopPropagation:取消事件进一步捕获或冒泡
事件委托
// html
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
// js
document.getElementById("ul").onclick = function (event) {
var target = event.target
if (target.nodeName.toLowerCase === 'li') {
alert(target.innerHTML)
}
}
AJAX
2005年开始,ajax作为一项新兴的交互开始影响web的发展,ajax的核心是XMLHttpRequest对象。
var xhr = new XMLHttpRequest()
// xhr.open接受三个参数,要发送的请求类型get post 请求的url、是否异步发送的布尔值
xhr.open('get', '/url', true)
// 调用send函数发送这个请求,参数为携带的参数
xhr.send(null)
// 通过onreadystatechange事件监听当前xhr实例的阶段,通过判断xhr.readyState的阶段,来判断当前请求的状态
// 0:未调用open方法
// 1:已调用open方法 未调用send方法
// 2:已调用send方法但尚未收到返回
// 3:已收到部分响应数据
// 4:已收到所有响应数据
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.responseText)
}
}
ES6 之后的fetch API
在ES6之后,浏览器端新增了一个fetch api 有以下几个特点
- 返回一个promise结果
- 默认不带cookie,需要使用配置credentials: ‘include’
- 当网络故障或请求被阻止时,才会标记为reject,否则即时返回状态码是500,也会resolve这个promise
fetch('/ajax.json').then(function (response) {
response.text() // 返回字符串
response.json() // 返回json
response.blob() // 一般指返回文件对象
response.arrayBuffer() // 返回一个二进制文件
response.formData() // 返回表单格式内容
})