20240229笔记
浏览器预加载器
手动:prefetch preload
会尽早加载但不会过早执行script
自动:浏览器自身的智能预测和预先加载下一页内容的功能。浏览器内部策略决定。
动态加载脚本
通过script标签实现
缺点:对浏览器预加载器是不可见的
https2优点
- 多路复用
- 二进制分帧
- 头部压缩
- 服务器推送
- 优先级和流量控制
下载图片并不一定要将image添加到dom中,只要添加了src就会下载。
下载Js需要添加src,并添加到dom中。
load事件和DOMContentLoaded事件的区别
load:整个页面的所有资源已完成加载后触发。
DOMContentLoaded:HTML文档被完全加载和解析完成时触发,不用等样式表、img、js、其他资源加载完毕。在load之前触发。
代理模式的作用
1.跟踪属性的访问,设置get set函数,来跟踪什么时候去获取属性,什么时候设置属性,并进行拦截
2.隐藏属性,在get set函数中进行判断,来隐藏属性,外部不能访问这些属性
3.属性验证,在get set函数中,set时进行属性验证,来决定是否允许还是拒绝赋值
4.函数参数与构造函数参数的验证
在apply中,判断arguments参数的类型
在construct中,判断arguments参数的类型
5.数据绑定与可观察对象
在construct中,将所有的proxy对象添加到list集合中,再把集合通过new Proxy中的set函数,来进行事件绑定程序
代理是object的透明抽象层,代理的反射Reflect的api,封装了拦截的相对应的方法
const target = { foo: "bar", }; const handler = { //定义一个get捕获器,在获取属性和Object.create时候会调用,在代理对象上执行操作时才会调用,在目标对象上执行操作是正常行为 get() { return "handler"; }, }; const proxy = new Proxy(target, handler); console.log(target.foo); console.log(proxy.foo); console.log(Object.create(target)['foo']); console.log(Object.create(proxy)['foo']);
捕获器参数和反射api
const target = { foo:'bar' } const handler = { //get捕获器的参数get(target,property[,receiver]) get(trapTarget,property,receiver){ console.log(trapTarget===target); console.log(property); console.log(receiver===proxy); return trapTarget[property]; } } const proxy = new Proxy(target,handler); console.log(target.foo); console.log(proxy.foo);
空代理对象:指的是使用反射Reflect的api来定义捕获器
const target = { foo:'bar' } const handler = { //get捕获器的参数get(target,property[,receiver]) // get(trapTarget,property,receiver){ // return Reflect.get(...arguments) // } //简洁的写法 get:Reflect.get } const proxy = new Proxy(target,handler); //捕获所有方法,利用Reflect转发给对应反射API的空代理来实现 const proxy1 = new Proxy(target,Reflect); console.log(target.foo); console.log(proxy1.foo); console.log(proxy.foo); const target = { foo:'bar', baz:'121212' } const handler = { //get捕获器的参数get(target,property[,receiver]) get(trapTarget,property,receiver){ let decoration = ''; if(property==='foo'){ decoration='!!!'; } return Reflect.get(...arguments)+decoration;//获取foo和baz属性时,显示的结果不一样 } } const proxy = new Proxy(target,handler); console.log(target.foo); console.log(proxy.foo); console.log(target.baz); console.log(proxy.baz);
捕获器不变式 每个捕获的方法都知道目标对象的上下文,捕获函数签名
target的属性不可配置时,在捕获器中去修改属性会报错
const target = {}; Object.defineProperty(target,'foo',{ configurable:false, writable:false, value:'bar' }) const handler = { get(){ return 'quz' } } const proxy = new Proxy(target,handler); console.log(proxy.a); // console.log(proxy.foo);
可撤销代理 Proxy.revocable方法
const target = { foo:'bar' } const handler = { get(){ return 'inter' } } const {proxy,revoke} = Proxy.revocable(target,handler);// 撤销函数和代理对象是在实例化的时候同时产生的 console.log(proxy.foo); revoke();//撤销后,Proxy与target之间的管理取消,而且不可恢复 console.log(proxy.foo);
使用反射API
- 反射API与对象API
1.反射api并不限于捕获处理程序
2.大多数反射API在Object类型上有对应的方法
Object上的方法适用于通用程序,反射方法适用于细粒度的对象控制与操作
- 状态标记:表示执行操作是否成功,return true/false
Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.deleteProperty()
- 用一等函数替代操作符
Reflect.get():替代属性访问操作符
Reflect.set():替代=赋值操作符
Reflect.has():替代in或者with()
Reflect.deleteProperty():替代delete操作符():
Reflect.construct():替代new操作符
- 安全的应用函数
Reflect.apply(myFunc,this.val,argumentsList)
代理另一个代理
const target = { foo: "bar", }; const firstProxy = new Proxy(target,{ get(){ console.log('first proxy'); return Reflect.get(...arguments); } }) const secondProxy = new Proxy(firstProxy,{ get(){ console.log('second proxy'); return Reflect.get(...arguments);q } }) console.log(secondProxy.foo);
代理的问题与不足
- this的执行问题 解决: 重新配置代理,把代理实例改为代理本身
- 代理与内部槽位 有些内置ECMAScript内置类型可能会依赖代理无法控制的机制,比如Date类型方法的执行依赖this值上的内部槽位[[NumberDate]],且不能通过get set来访问
const target = { thisValEqualsProxy() { return this === proxy; }, }; const proxy = new Proxy(target, {}); console.log(target.thisValEqualsProxy());//false console.log(proxy.thisValEqualsProxy());//true const target = new Date(); const proxy = new Proxy(target,{}); console.log(target instanceof Date); target.getDate(); console.log(proxy instanceof Date); proxy.getDate();//报错
BOM
- window对象
- location 对象
获取query参数
let getQueryStringArgs = function () { //?a=1&b=2 let location = {}; location.search = "?a=1&b=2"; let qs = location.search.length > 0 ? location.search.substring(1) : ""; let args = {}; //[a=1,b=2] qs.split("&") .map((item) => item.split("=")) .map((item) => { let name = decodeURIComponent(item[0]), value = decodeURIComponent(item[1]); name.length > 0 ? (args[name] = value) : ""; }); console.log(args); return args; }; let res = getQueryStringArgs(); console.log(res.c);
encodeURL encodeURIComponent
encodeURL对整个url进行编码,但是会保留某些字符不变,不会对=&编码,适用于整个URL编码
更严格的编码,会编码所有非字母数字符号,确保所有引起解析问题的字符都被正确编码,适用于URL的各个部分编码
let url = 'https://example.com/path?name=John Doe&age=30'; console.log(encodeURI(url)); console.log(encodeURIComponent(url)); let name = encodeURIComponent('j A'); let age = encodeURIComponent(30); let query = `name=${name}&age=${age}`; console.log(query);
URLSearchParams构造函数,来创建search实例,实例有set get delete has方法,且实例是迭代对象
const s= new URLSearchParams('?a=1&b=2'); console.log(s.get('a')); s.set('c',3); console.log(s.has('a')) s.delete('a');
navigator对象 浏览器对象
history
history.pushState() history.replaceState() popstate事件
客户端检测
- 能力检测:在js运行时中使用一套简单的检测逻辑,测试浏览器是否支持某种特性。
基于能力检测进行浏览器分析:
- 检测特性
- 检测浏览器
- 能力检测的局限:适合用于决定下一步该怎么做,不一定能够作为辨识浏览器的标志。
- 用户代理检测:通过浏览器的用户代理字符串确定使用的是什么浏览器。用户代理字符串包含在每个http请求的头部。
navigator.userAgent
- 伪造用户代理
- 分析浏览器
- 软件与硬件检测
navigator.vendor 浏览器开发商信息
navigator.platform 浏览器所在的操作系统
screen.colorDepth screen.pixelDepth 设备中每像素用于显示颜色的位数
screen.orientation 屏幕信息
浏览器元数据
- Geolocation API
navigator.geolocation.getCurrentPosition()获取position对象
- connection state和NetworkInformation API
navigator.online
online事件和offline事件
navigator.connection 连接对象
navigator.connection.onchange事件
- Battery Status API
navigator.getBattery().then().catch()
- 硬件
- navigator.hardwareConcurrency:处理器核心数,不一定是实际的CPU核心数,而是表示浏览器可以并行执行的最大工作线程数量。
- navigator.deviceMemory:设备内存大小,>8时还是显示为8。
- navigator.mediaDevices:获取设备最大触摸点数。
- Geolocation API
DOM:文档对象模型
是W3C制定的一系列接口和协议,用于标识XML或HTML文档的结构,允许开发者通过js等脚本语言动态访问和操作网页内容
- DOM 1:document.createElement() document.getElementById() node.appendChild()
- DOM 2:增加了对css样式操作的支持element.style,遍历文档树NodeIterator,TreeeWalker接口,事件处理模型addEvenetListener(),removeEventListener()
- DOM 3:DOMParser XMLSerializer,命名空间,DocumentType,Notation,Entity等节点接口,文本范围操作,完善选择器API,允许使用css选择器来查找元素
DOM:节点层级
document:根节点
元素:html文档元素,为根节点的唯一子节点
DOM中有12种节点类型,这些类型都继承一种基本类型
- Node类型
Node.ElEMENT_NODE(1) someNode.nodeType==Node.ELEMENT_NODE
1. nodeName与nodeValue属性 2. 节点关系:每个节点都有childNodes属性,为一个NodeList的实例,是一个实时的活动对象。一个节点不会在文档中有两个位置。 length,hasChildNodes(),firstChild,lastChild,previousSibling,nextSibling,patentNode 3.操作节点:appendChild(),insertBefore(),replaceChild(),removeChild(),cloneNode(false/true表示深复制还是浅复制)
- Document类型:文档对象document是HTMLDocument的实例。
文档子节点:
document.documentElement //html document.body //body document.doctype //doctype
文档信息document.title document.URL document.domain document.referrer
定位元素document.getElementById() document.getElementByTagName() document.getElementByTagName('div')['value']即为document.getElementByTagName('div').namedItem('value') document.getElementByTagName('*') document.getElementsByName()
文档写入document.write() //在window.onload之后write则会覆盖全Html document.writeIn() document.open() document.close()
3. Element类型 ```javascript div.nodeType //1 div.tagName==div.nodeName//true div.id div.title div.className div.dataset div.getAttribute('class')//getAttribute用于取得自定义属性的值 div.getAttribute('id') div.getAttribute('title') div.getAttribute('xx') div.setAttribute(key,value)//设置属性 div.abcd=value//通过此种方法设置的属性无法通过getAttribute获取 div.removeAttribute(key) div.attributes//是一个NamedNodeMap实例 div.attributes.getNamedItem(name) div.attributes.removeNamedItem(name) div.attributes.setNamedItem(node) div.attributes.getNamedItem('id').nodeValue//返回的是id的值 div.attributes['id'].nodeValue//返回的是id的值 div.attributes['title'].nodeValue='1'//设置title的值为1 document.createElement('div') element.childNodes ``` 4. Text类型 ```javascript div.nodeType //3 div.appendData(text) div.insertData(offset,text) div.deleteData(offset,count) div.replaceDate(offset,count,text) div.splitText(offset) div.substringData(offset,count) div.length document.createTextNode(text) element.normalize()//规范化文本节点,即合并两个相邻文本节点 ``` 5. Comment类型 ```javascript div.nodeType //8 div.data//为注释的文字 document.createComment('这是注释') ``` 6. CDATASection类型 7. DocumentType类型 ```javascript div.nodeType //10 document.doctype// document.doctype.name//html ``` 8. DocumentFragment类型 ```javascript div.nodeType //11 document.createDocumentFragment() ``` 9. Attr类型 ```javascript div.nodeType //2 let attr = document.createAttribute('align') attr.value="left" element.setAttributeNode(attr) element.attributes['align'].value//left element.getAttributeNode('align').value//left element.getAttribute('align')//left //推荐使用getAttribute() removeAttribute() setAttribute()来操作属性 ```
DOM编程
- 动态脚本
document.createElement('script')
- 动态样式
document.createElement('link')
NodeList是基于DOM文档的实时查询,使用时需缓存NodeList - MutationObserver接口,可观察的范围包括属 性变化、文本变化和子节点变化。可添加观察范围的白名单。
使用:
let observer = new MutationObserver(()=>console.log('change')) observer.observe(document.body,{attributes:true})
回调与MutationRecordlet observer = new MutationObserver((mutationRecords,mutationObserver)=>console.log(mutationRecords,mutationObserver)); observer.disconnect()//提前终止执行回调,一刀切。终止后可重新关联 observer.observe(childA,{attributes:true}); observer.observe(childB,{attributes:true});//observer可复用
- MutationObserverInit与观察范围:
- 观察属性
attributes:true //所有的属性
attributeFilter:[‘foo’]//白名单
attributeOldValue:true//是否保存旧值
- 观察字符数据:Text Comment 的添加、删除和修改
characterData:true
characterDataOldValue:true
- 观察子节点
childList:true
- 观察子树
subtree:true
- 观察属性
- 异步回调与记录队列:每个队列对每一个mutationObserver实例都是唯一的,是所有DOM变化事件的有序列表
记录队列:回调执行后,MutationRecord用不到了,记录队列会被清空,内容会被丢弃
takeRecords:
let observer = new MutationObserver((mutationRecords, mutationObserver) => { console.log(mutationRecords, mutationObserver); }); observer.observe(document.body,{ attributes:true }); document.body.className='a'; document.body.className='b'; document.body.className='c'; console.log(observer.takeRecords());//返回所有的MutationRecords实例,并清空 记录队列 //observer.takeRecords();//希望断开与目标对象的联系,但又希望处理由于调用disconnect()而被抛弃的记录队列中的MutationRecord实例 console.log(observer.takeRecords());
- 性能、内存与垃圾回收
- MutationObserver的引用
MutationObserver实例与目标节点之间的引用关系是非对称的。
MutationObserver对目标节点是弱引用,不会妨碍垃圾回收程序回收目标节点。
目标节点对MutationObserver是强引用,目标节点被删除被回收,则MutationObserver也会被垃圾回收。
- MutationRecord的引用
每个MutationRecord至少包含对已有DOM节点的一个引用。记录队列和回调处理的默认行为是耗尽这个队列,并被回收。建议从MutationRecord中抽取最有用的信息并保存在新对象中,最后抛弃MutationRecord。
- MutationObserver的引用
重绘和回流
- 重绘:dom外观变化,但不影响其在文档流中的位置时,需要重绘。修改颜色、背景色、border样式等视觉属性,会重绘dom及其子元素的像素,不会改变页面布局。
重绘比回流快,只涉及视觉层面的更新。
- 回流:回流也称为布局或重排。DOM的position、size、display变化时,需要重新计算dom的布局、宽高等,并可能导致其他dom的position变化,触发回流。
回流操作代价大,浏览器要重新计算受影响的dom的几何信息和相关节点,可能引发后续的重绘。
触发回流的操作:add/delete dom、修改width/height/fontSize/margin/padding等影响布局的熟悉,window的resize等。
优化网页性能,尽量减少不必要的重绘和回流:
- 动态需要更改的样式,尽量使用css类进行批量修改而非直接操作style属性。
- 将涉及布局的样式改动集中在一起一次性更新,而不是频繁的分散修改。
- 使用css3硬件加速特性来提升渲染性能。如transform/opacity/will-change/backface-visibility/position:fixed||sticky/composite