一个完整的JavaScript实现应该由三部分组成 —— ECMAScript(核心),DOM(文档对象模型),BOM(浏览器对象模型)。
由ECMA-262定义的ECMAScript与Web浏览器没有依赖关系。ECMAScript定义的只是这门语言的基础。
我们常见的Web浏览器只是ECMAScript实现可能的宿主环境之一。我们学习的这个是浏览器端的JS,而nodejs中就没有BOM的定义,因为nodejs不是跑在浏览器中的,而是服务端的。
BOM,是 Browser Object Model 的缩写,即浏览器对象模型。而这个BOM是用来做什么的,是提供与浏览器交互的方法和接口(即访问和操作浏览器)。
window
,表示浏览器的一个实例 Global
对象。navigator
对象location
对象screen
对象history
对象location对象的所有属性
toString()
方法也返回这个值http:
或https:
一个小例子
// 地址:https://www.baidu.com:8080/page/?tn=22073068_9_oem_dg#test
location.href // https://www.baidu.com:8080/page/?tn=22073068_9_oem_dg#test
location.protocol // https:
location.host // www.baidu.com:8080
location.hostname // www.baidu.com
location.port // 8080
location.pathname // /page/
location.search // ?tn=22073068_9_oem_dg
location.hash // #test
面试题:提取url中查询参数
function getQueryStringArgs () {
// 取得查询字符串并去掉开头的问号
var qs = location.search.length > 0 ? location.search.slice(1) : '';
// 保存数据的对象
var args = {};
// 保存每一项
var items = qs.length ? qs.split('&') : [];
var item = null;
var key = null;
var value = null;
// 逐个将每一项添加到args对象中
for (var i = 0, len = items.length; i < len; i++) {
item = items[i].split('=');
key = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if (key.length > 0) {
args[key] = value;
}
}
// 返回args对象
return args;
}
可以用来识别客户端浏览器(和浏览器的一些插件)。这在工作开发中很常见。现在浏览器很多很杂,我们开发时难免要为某一个特别一点的浏览器做一些特别的处理,这个时候,识别浏览器就很重要了。特别是移动端,ios和android差别不是一般的大,所以这个时候可以使用navigator
对象去识别。
一个小例子
var ua = navigator.userAgent;
var isChrome = ua.indexOf('Chrome');
console.log(isChrome); // 打印匹配到的字符的位置
// 下面的是直接打印navigator.userAgent的值
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
var isIos = ua.indexOf('iPhone');
console.log(isIos); // 打印匹配到的字符的位置
// 下面的是直接打印navigator.userAgent的值
// Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
screen
对象时包含浏览器窗口外部的显示器的信息。这个在PC端开发没什么大用出。但是在移动端的开发就很有用了。具体的去看文档吧——Screen MDN
history
对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。
history.go()
方法可以在用户的历史记录中任意跳转。这个方法接收一个参数,表示向后或者向前跳转的页面数的一个整数值。负数表示向后跳转,整数表示向前跳转。也可以给该方法传递一个字符串参数,此时浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进,具体要看哪个位置最近。// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 跳转到最近的 https://www.baidu.com 页面
history.go('https://www.baidu.com');
back()
和forward()
来代替。顾名思义,可以模仿浏览器中的“后退”和“前进”按钮。// 后退一页
history.back();
// 前进一页
history.forward();
DOM,是 Document Object Model 的缩写,即文档对象模型。是针对XML但经过扩展用于HTML的应用程序编程接口(API,Application Programming Interface)。DOM把整个页面映射为一个多层节点结构(一种树数据结构)。
DOM核心规定的是如何映射基于XML的文档结构,以便简化对文档中任意部分的访问和操作。
document.getElementById
:根据ID查找元素,大小写敏感document.getElementsByTagName
:根据标签查找元素, * 表示查询所有标签,返回一个 HTMLCollectiondocument.getElementsByClassName
:根据类名查找元素,多个类名用空格分隔,返回一个 HTMLCollectiondocument.getElementsByName
:根据元素的name属性查找,返回一个 NodeListdocument.querySelector
:返回单个Node,如果匹配到多个结果,只返回第一个document.querySelectorAll
:返回一个 NodeListelement.setAttribute(name, value);
其中name是特性名,value是特性值。如果元素不包含该特性,则会创建该特性并赋值。var value = element.getAttribute('id');
返回指定的特性名相对应的特性值。如果不存在,则返回 null 。var className = div.className;
var align = div.align;
var id = div.id;
div.className = 'sidebar';
div.align = 'center';
Node.parentNode
,返回当前节点的父节点Node.childNodes
,返回当前节点的所有子节点Node.firstChild
,返回当前节点的第一个子节点Node.lastChild
,返回当前节点的最后一个子节点Node.appendChild(node)
,向节点添加最后一个子节点Node.removeChild(node)
,删除节点,在要删除节点的父节点上操作查看详情
时,会执行并阻塞渲染div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
理论上会发生4次重排,实际上只发生了1次重排。因为现代的浏览器都有渲染队列的机制,当改变元素的一个样式会导致浏览器发生重排或者重绘时,它会进入一个渲染队列,直到下面没有样式修改,浏览器会按照渲染队列批量执行来优化重排过程,一并修改样式。
但下面的写法就不一样了,因为这样发生了4次重排
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
因为有一些属性是会强制刷新队列要求样式修改任务立即执行(毕竟浏览器不确定在接下来的代码中是否还会修改同样的样式,为了保证获得正确的值,必须立刻执行渲染队列触发重排)
以下为触发刷新渲染队列的属性或方法
1. 分离读写操作
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
2. 样式集中修改
// bad
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
// good
div.style.cssText = 'left:10px;top:10px;width:20px;height:20px;';
// better
// 可维护性好,虽然有一点性能影响(需要检查级联样式),但瑕不掩瑜
div.className = 'new-class';
3. 缓存布局信息
// bad
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
// good
// 相当于分离读写操作
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
4. 元素批量修改
减少重绘和重排的原理
小例子:向ul中循环添加大量li
// bad
var ul = document.getElementById('demo');
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text);
ul.appendChild(li);
}
// good
var ul = document.getElementById('demo');
ul.style.display = 'none';
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text);
ul.appendChild(li);
}
ul.style.display = 'block';
// good
var ul = document.getElementById('demo');
var frg = document.createDocumentFragment();
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text);
frg.appendChild(li);
}
ul.appendChild(frg);
// good
var ul = document.getElementById('demo');
var clone = ul.cloneNode(true);
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text);
clone.appendChild(li);
}
ul.parentNode.replaceChild(clone,ul);
浏览器渲染大体上分为以下五步
浏览器把得到的html代码转化为一个DOM树,我们html文档中每一个tag标签都是一个DOM节点(文本节点也是),DOM树的根节点就是我们的document对象。这里要注意一下,js动态生成的DOM节点也在DOM树上
浏览器会把所有的样式解析为样式结构体(包括css样式和浏览器默认样式),最后生成了CSSOM树
Render树也叫作渲染树,它是由DOM树和CSSOM树合成的。渲染树的每一个节点都有自己的style样式。渲染树上没有隐藏的节点,因为这些节点不会呈现也不影响呈现。
会根据Render树的样式计算布局,输出的结果称为box盒模型。盒模型精确表示了每一个元素的位置和大小,所有的相对度量单位都转化为了绝对单位。
)加载过程中,有一些很有趣的东西原文出处
css
加载不会阻塞DOM
树的解析css
加载会阻塞DOM
树的渲染css
加载会阻塞后面js
语句的执行所以为了避免用户看到长时间的白屏时间,我们应该尽可能提高css加载速度,比如可以使用以下几种方法: