-
defer
脚本延迟执行,适用于外部脚本文件async
立即下载,不保证顺序(建议不修改DOM,避免重绘)
CDN加速 (Content Delivery Network,内容分发网络) 提高访问网站的响应速度 - 内存
js内存回收机制:解除引用
内存溢出:内存不够,系统会提示内存溢出,有时候会自动关闭软件
内存泄漏:未能释放已经不再使用的内存,不是指内存在物理上的消失,而是内存的浪费。
常见内存泄漏情形:1.全局变量、2.被忘记的Timers
或者callbacks
、3.闭包、4.DOM引用 - 闭包
概念:有权访问另一个函数作用域的变量的函数。
作用:匿名自执行函数、结果缓存、封装、实现类和继承 -
函数封装模式
//工厂模式:在函数中返回一个创建并返回一个对象 function createClass0(params){ var o = new Object(); o.name = params; o.fn0 = function(){ console.log(this.name); } return o; } var obj = createClass0(params); console.log(obj.constructor == createClass0); //false console.log(obj instanceof createClass0); //false //构造函数模式:es6的类也是通过该模式实现 function Class0(params){ this.name = params; this.fn0 = fn0; this.fn1 = function(){ console.log(this.name) } } function fn0(){ console.log(this.name) } var obj = new Class0(params); console.log(obj.constructor == Class0); //true console.log(obj instanceof Class0); //true //原型模式:属性与方法挂在原型链上 function Class0(){} Class0.prototype = { name: params, fn0: function(){ console.log(this.name) } } var obj = new Class0(); console.log(obj.constructor == Class0);//true console.log(obj instanceof Class0); //true Object.defineProperty(Class0.prototype,"constructor",{ enumerable: false, value: Class0 }) console.log(obj.constructor == Class0); //false //动态原型模式: function Class0(params){ this.name = params; if(typeof this.fn0 != "function"){ Class0.prototype.fn0 = function(){ console.log(this.name) } } } var obj = new Class0(params); console.log(obj.constructor == Class0); //true console.log(obj instanceof Class0); //true //寄生构造函数模式: function Class0(params){ var o = new Object(); o.name = params; return o; } var obj = new Class0(params); console.log(obj.constructor == Class0); //false console.log(obj instanceof Class0); //false //稳妥构造函数模式:无法访问其属性 function Class0(params){ var o = new Object(); var name = params; o.fn0 = function(){ console.log(name); } return o; } var obj = Class0(params); console.log(obj.constructor == Class0); //false console.log(obj instanceof Class0); //false //继承:js没有借口继承,只有实现继承,依靠原型链实现 //经典继承:借用构造函数 function Class0(params){ this.name0 = params; this.fn0 = function(){ console.log(this.name); } } function subClass0(params){ Class0.call(this,params); this.name1 = params; subClass0.prototype.fn1 = function(){ console.log(this.name1); } } var obj = new subClass0(params); console.log(obj.constructor == subClass0); //true console.log(obj instanceof subClass0); //true console.log(obj.constructor == Class0); //false console.log(obj instanceof Class0); //false //组合继承:es6类的继承通过该模式实现 function Class0(params){ this.name0 = params; this.fn0 = function(){ console.log(this.name0); } } function subClass0(params){ Class0.call(this,params); this.name1 = params; subClass0.prototype.fn1 = function(){ console.log(this.name1); } } subClass0.prototype = new Class0(); subClass0.prototype.constructor = subClass0; var obj = new subClass0(params); console.log(obj.constructor == subClass0); //true console.log(obj instanceof subClass0); //true console.log(obj.constructor == Class0); //false console.log(obj instanceof Class0); //true
- BOM
概念:浏览器对象模型
核心对象:window
,通过js访问浏览器窗口的一个接口,也是ECMAScript
规定的Global
对象。
窗口框架:frame
、iframe
窗口位置:window.screenLeft/window.screenX,window.moveBy(x,y); window.moveTo(x,y);
窗口大小:window.resizeTo(width,height); window.resizeBy(dWidth,dHeight);
导航弹窗:let win = window.open("..."); win.close();
-
location
hash
:URL
的hash
字符串host
:服务器名称和端口号hostname
:服务器名称href
:当前加载页面的完整URL
pathname
:URL
中的目录port
:URL
中制定的端口号prptocol
:页面使用的协议search
:URL
的查询字符串,以?
开头
改变URL
会生成新记录,禁用历史记录跳转可通过location.replace("...")
实现
刷新页面:location.reload();
location.reload(true);
-
navigator
离线检测:onLine 地理信息:geolocation navigator.geolocation.getCurrentPosition((position)=>{ //成功的回调 },(error)=>{ //错误的回调 },{ enableHighAccuracy: true, //尽可能使用最准确的位置信息 timeout: 5000, //等待位置信息的最长时间 maximumAge: 25000 //上一次取得坐标信息的有效时间,时间到则重新获取 }); //跟踪监控 let watchId = navigator.geolocation.watchPosition(position)=>{ //成功的回调 },(error)=>{ //错误的回调 }); clearWatch(watchId); //关闭跟踪 浏览器中安装的插件信息:plugins //IE浏览器无效 function hasPlugin(name){ return Array.from(navigator.plugins).some(item => item.name.toLowerCase().includes(name.toLowerCase()) ) } hasPlugin("Flash"); //IE浏览器 function hasIEPlugin(name){ try{ new ActiveXObject(name); return true; }catch(ex){ return false; } } hasIEPlugin("ShockwaveFlash.ShockwaveFlash"); cookie是否启用:cookieEnabled 浏览器所在的系统平台:platform 用户代理:userAgent(可用于判断浏览器引擎、版本、终端等) //简易的浏览器类型判断 function browserType(){ //判断浏览器类型 let userAgent = navigator.userAgent; let type; switch(userAgent){ case userAgent.includes("MSIE"): type = "IE"; break; case userAgent.includes("Firefox"): type = "Firefox"; break; case userAgent.includes("Chrome"): type = "Chrome"; break; case userAgent.includes("Opera"): type = "Opera"; break; case userAgent.includes("Safari"): type = "Safari"; break; case userAgent.includes("Netscape"): type = "Netscape"; break; default: console.log(userAgent); break; } return type; }
-
history
页面前进:history.forward(); history.go(1);
页面后退:history.back(); history.go(-1);
跳转至可能存在的最近某个页面(不存在则什么都不做):history.go("...");
历史记录数量:history.length
(hash
改变会使历史记录增加) -
系统对话框
alert(val); //警告窗 confirm(val); //确认窗,选择OK,则返回true prompt(val,""); //输入窗,选择OK,则返回输入框内容,否则返回null
-
数据存储
cookie
:信息越大,对服务器的请求时间越长,故浏览器对其大小有限制,总量不到4K//创建cookie function setCookie(name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value);//防止中文乱码,所以需要编码 (expires instanceof Date) && (cookieText += '; expires=' + expires); //有效期 path && (cookieText += '; path=' + path); //设置域的路径 domain && (cookieText += '; domain=' + domain); //设置哪个域的请求中都会包含该cookie信息,默认为设置cookie的域 secure && (cookieText += '; secure'); //只有在使用SSL连接的时候才发送到服务器 document.cookie = cookieText; } //获取cookie function getCookie(name) { var cookieName = encodeURIComponent(name) + '='; var cookieStart = document.cookie.indexOf(cookieName); var cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(';', cookieStart); if (cookieEnd == -1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)); } return cookieValue; } //删除cookie function unsetCookie(name) { document.cookie = name + "= ; expires=" + new Date(0); }
localStorage
:本地存储,容量5M左右,值类型限定为string
类型,localStorage
本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡,另外在部分浏览器隐私模式下不可读取。sessionStorage
:会话存储(刷新页面依旧存在),与localStorage
在持久上不同外,其余一致。indexedDB
:储存空间不少于250M,没有上限,异步设计,支持二进制储存(ArrayBuffer
对象和Blob
对象)localforage
:js库,异步操作(自动加载最佳驱动程序,从IndexedDB
到localStorage
)//存储 localforage.setItem('key', 'value'); localforage.setItem('key', 'value').then(()=>{ ... }).catch(err=>{ console.log(err); }); localforage.setItem('key', 'value').then(()=>{ return localforage.getItem('key'); }).then(val=>{ console.log(val); }).catch(err=>{ console.log(err); }); //获取 localforage.getItem('key').then(()=>{ ... }).catch(err=>{ console.log(err); }); //删除 localforage.removeItem('key'); localforage.removeItem('key').then(()=>{ ... }).catch(err=>{ console.log(err); }); //清空 localforage.clear(); localforage.clear().then(()=>{ ... }).catch(err=>{ console.log(err); }) //遍历 localforage.iterate((value, key)=>{ console.log(key,value); }).then(()=>{ ... }).catch(err=>{ console.log(err); });
-
DOM
//querySelector选择器,返回匹配的一个元素 let parentElement = document.querySelector(".container"); //创建元素 let element = document.createElement("button"); //设置元素的属性 element.setAttribute("onclick", `javascript:alert('click the span!');`); //设置内容,innerText有兼容性问题,采用innerHTML较为方便 element.innerHTML = "创建元素添加的元素"; //设置内联样式 element.style.color = "blue"; //在容器中末尾加入元素 parentElement.appendChild(element); //添加元素的另一种方式,该方式适合一次性创建多节点 let elementHtml = `` parentElement.innerHTML += elementHtml; //元素的克隆.cloneNode(true),父节点.parentNode,首个子节点.firstChild、下一个兄弟节点.nextElementSibling,加入到某个元素的前面refNode.parentNode.insertBefore(newNode,refNode) parentElement.insertBefore(parentElement.parentNode.cloneNode(true), parentElement.firstChild.nextElementSibling);//克隆一份parentElement的父节点,并将其放在parentElement的第一个子元素的下一个元素的前面 let newElement = element.cloneNode(true); newElement.innerHTML = "newElement"; //容器的最后一个子元素.lastChild,上一个兄弟节点.previousElementSibling,替换元素refNode.parentNode.insertBefore(newNode,refNode) parentElement.replaceChild(newElement, parentElement.lastChild.previousElementSibling);//替换parentElement最后一个子元素的上一个元素 //querySelectorAll选择器返回NodeList let firstContainer = document.querySelectorAll(".container")[0]; //删除元素node.parentNode.removeChild(node),元素子节点.children firstContainer.removeChild(firstContainer.children[0]);//删除firstContainer这个元素的第一个子元素 //动态添加样式规则 document.querySelector("head").innerHTML += `` //移动滚动条至firstContainer这个元素可见,true或不传表示元素顶端对齐,false表示底端在可视区域中间 firstContainer.scrollIntoView(true); //获取元素的真正样式 window.getComputedStyle(element) console.log(window.getComputedStyle(firstContainer)) //获取元素的真正样式 console.log(firstContainer.style); //获取元素的内联样式 //获取元素的尺寸及相对于浏览器可视窗口的位置(非文档,故页面发生滚动会改变).getBoundingClientRect(),在计算坐标使用时可用clientX配合getBoundingClientRect().left console.log(firstContainer.getBoundingClientRect());
- 事件流
事件流过程:捕获过程、目标、冒泡过程
事件捕获:从大到小,极少使用
事件冒泡:从小到大,常用,虽然各浏览器有些差异
阻止冒泡:e.stopPropagation()
,IE9前使用e.cancelBubble=true
阻止默认行为:e.preventDefault()
,IE9前使用e.returnValue=false
默认行为:如表单按钮的提交,标签的跳转等
事件委托:利用事件冒泡,监听顶级的DOM(事件处理程序数量关系到页面的整体运行性能) -
页面事件
pageshow
事件:页面显示时触发,事件目标为document
,但必须将其事件处理程序添加到window
,参数为event
pagehide
事件:页面卸载时触发,事件目标为document
,但必须将其事件处理程序添加到window
,参数为event
haschange
事件:URL
的参数列表发生变化时触发,但必须将其事件处理程序添加到window
,参数为event
visibilitychange
事件:绑定在document
上,当页面最小化或切换浏览器标签等可见状态改变触发,documen.hidden
可查看页面可见度状态document.addEventListener('visibilitychange',function(){ console.log(document.hidden); })
- 表单事件
默认行为:表单中的最后一个按钮会自动提交表单form.submit()
主动提交表单form.reset()
重置表单form.checkValidity()
表单验证配合require和pattern使用noValidate
属性禁用表单验证
过滤输入,屏蔽某些词的默认行为:监听keypress
事件,e.preventDefault();
富文本编辑的实现:
①iframe
实现,空文档的designMode
设置为on
,getSeclection
事件获取具体信息;
②设置contenteditable
属性实现,通过document.execCommand()
实现富文本操作,如document.execCommand("blod", false, null);
实现粗体文本。 - 鼠标事件
双击事件顺序:mousedown
、mouseup
、click
、mousedown
、mouseup
、click
、dblclick
移入容器事件顺序过程:mouseover
(在不同元素间触发)、mouseenter
、mousemove
、mouseout
(在不同元素间触发)、mouseleave
右键点击事件:contextmenu
(H5事件)
位置信息:
clientX
(鼠标相对于浏览器窗口可视区域的X坐标)
offsetX
(鼠标相对于事件源元素的X坐标,存在兼容性问题)
screenX
(鼠标相对于用户显示器屏幕左上角的X坐标)
pageX
(鼠标相对于文档区域的X坐标,存在兼容性问题)
event对象属性:
type
(事件类型)
detail
(点击次数)
target
(事件源元素)
path
(数组,冒泡的路径) -
拖放事件
相关事件:dragstart、drag、dragend、dragenter、dragover、dragleave、drop
投放目标 被拖拽的物体dragover(e){ e.preventDefault(); } drag(e){ e.dataTransfer.setData("id",e.target.id); } drop(e){ e.preventDefault(); let data = e.dataTransfer.getData("id"); e.target.appendChild(document.getElementById(data)); } - 触摸与手势事件
移动端点击事件顺序:touchstart
、touchend
、mouseover
、mouseenter
、mousemove
、mousedown
、mouseup
、click
touchmove
事件:需要阻止默认行为以防止屏幕滚动touchcancel
事件:当系统停止跟踪触摸时触发gesturestart
事件:当一个手指已经按在屏幕上而另一个手指又触摸时触发gesturechange
事件:当触摸屏幕的任何一个手指的位置发生变化时改变gestureend
事件:当任何一个手指从屏幕上面移开时触发
触摸事件特有的event对象属性:
touches
(表示当前跟踪的触摸操作的Touch对象的数组)
targetTouchs
(特定于事件目标的Touch对象的数组)
changeTouches
(上次触摸以来发生了什么改变的Touch对象的数组)
rotation
(手势变化引起的旋转角度,从0开始,顺正逆负)
scal
(手指间的距离变化,从1开始,与距离呈正相关) -
模拟事件
UIEvents
UI事件MouseEvents
鼠标事件MutationEvents
DOM变动事件HTMLEvents
HTML事件模拟鼠标点击事件: 1.创建鼠标事件的event对象 let event = document.createEvent("MouseEvents"); 2.设置触发的参数 //可简写,如event.initMouseEvent('click'); event.initMouseEvent(事件类型,...event对象其他参数); /*参数顺序:type、bubbles(是否冒泡)、cancelable(事件是否可取消)、view(document.defaultView)、detail、screenX、screenY、clientX、clientY、ctrlKey(是否按下ctrl)、altKey(是否按下alt)、shiftKey(是否按下shiftKey)、metaKey(是否按下metaKey)、button(表示按下哪个鼠标键)、relatedTarget(事件相关的对象,用于模拟mouseover/mouseout)*/ 3.触发事件 document.getElementById('btn').dispatchEvent(event);
- 媒体元素
音频播放器:audio
视频播放器:video
通过play()
和pause()
可手工控制媒体文件的播放和暂停,ended
事件当播放结束时触发 -
绘图技术
canvas
:2D绘图,可进行图片与图像的互转,通过变换和合成绘制复杂图像//获取canvas内容生成的URI let imgURI = canvas.toDataURL("image/png"); let img = document.createElement("img"); img.src = imgURI; document.body.appendChild(img);
svg
:支持事件驱动,通过foreignObject
可实现svg
和普通HTML
元素的融合WebGL
:3D绘图,非W3C标准 -
ajax
Asynchronous Javascript And XML
,即异步JavaScript和XML,是一种创建交互式网页应用的网页开发技术。//封装ajax function ajax(obj) { var xhr = new XMLHttpRequest(); //创建XMLHttpRequest实例 obj.url = obj.url + '?rand=' + Math.random(); //为url加时间戳 if(obj.method === 'get') obj.url += '&' + obj.data; if(obj.async) xhr.onreadystatechange = ()=>{ (xhr.readyState == 4) && callback(); } //异步请求当readyState == 4才执行回调 xhr.open(obj.method, obj.url, obj.async); //发送请求 if(obj.method === 'post'){ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(obj.data); }else{ xhr.send(null); } if(!obj.async) callback(); //同步请求 function callback() { if(xhr.status == 200){ obj.success(xhr.responseText); } //回调传递参数 else{ console.log('获取数据错误!错误代号:' + xhr.status + ',错误信息:' + xhr.statusText); } } } ajax({ method : 'post/get', url : '...', data : "", success : (res)=>{ console.log(res); }, async : true });
- get与post请求
get
请求:从指定的资源请求数据,数据量和类型有限制(因为数据放在请求头中,浏览器对其长度有限制),会被缓存,且数据在url
上可见,导致安全性相对低点。post
请求:向指定的资源提交被处理的数据,数据量和类型没限制,不主动缓存,页面刷新数据会被重新提交。
底层分析:对于get请求,浏览器会把http header
和data
一并发送出去,服务器响应200(返回数据),整个过程产生一个TCP数据包;对于post请求,浏览器先发送header
,服务器响应100 continue,浏览器再发送data
,服务器响应200 ok(返回数据),整个过程产生两个TCP数据包,导致时间消耗,网络环境畅通情况下可无视。 -
File API
let reader = new FileReader(); reader.readAsText(file,encoding); //以纯文本的形式读取文件,读取到的文本保存在result属性中 //reader.readAsDataURL(file); //读取文件以数据URI的形式保存在result属性中 //reader.readAsBinaryString(file); //读取文件并将一个字符串保存在result属性中,字符串中的每个字符表示一字节 //reader.readAsArrayBuffer(file); //读取文件并将一个包含文件内容的ArrayBuffer保存在result属性中 reader.progress = (event)=>{ if(event.lengthComputable){ console.log(event.loaded + '/' + event.total); } } reader.onerror = ()=>{ console.log(reader.error.code); } reader.onload = function(){ console.log(reader.result); }
-
对象防篡改
Object.preventExtensions(obj); //对象不可拓展,禁止添加属性或方法 Object.seal(obj); //密封对象,不可删除属性和方法 Object.freeze(obj); //冻结对象,不可拓展、且密封、不可编辑
-
String、JSON、Object
相互转化//js对象转JSON function jsToJSON(obj){ let objFnToStr = fnToStr(obj); return JSON.stringify(objFnToStr); } //字符串转JSON function strToJSON(str){ let obj = eval('('+str+')'); return jsToJSON(obj); } //JSON转js对象 function JSONToJs(json){ let obj = JSON.parse(json); return strToFn(obj); } //将js对象中的函数字符串解析为函数 function strToFn(obj){ var o = obj instanceof Array ? [] : {}; for(var k in obj) { switch(typeof(obj[k])){ case 'object': o[k] = obj[k] ? strToFn(obj[k]) : null; break; case 'string': o[k] = obj[k].match(/^\s*(function\s*\(.*\)\s*)|(\(.*\)\s*\=>\s*)/) ? eval('('+obj[k]+')') : obj[k]; break; default: o[k] = obj[k]; break; } } return o; } //将js对象中的函数类型转为字符串 function fnToStr(obj){ var o = obj instanceof Array ? [] : {}; for(var k in obj) { switch(typeof(obj[k])){ case 'object': o[k] = obj[k] ? fnToStr(obj[k]) : null; break; case 'function': o[k] = obj[k] + ''; break; default: o[k] = obj[k]; break; } } return o; }
-
定时器
//延迟调用 let timeId = setTimeout(fn,time); clearTimeout(timeId); //间歇调用 let timeId = setInterval(fn,time); clearInterval(timeId); //定时器中的定时器 setTimeout(function(){ //... setTimeout(arguments.callee, interval); }) //沉睡排序法 [1,5,2,4,100,3].forEach(n=>{ setTimeout(()=>{ console.log(n) }, n); })
-
html
与文本的转换//字符串转html字符串,参数:html传入的字符串,type是否返回标签结构,默认只返回内容,escape表示是否需要转义 function escapeHtml(html,type,escape){ //type:content/html,escape:转义/不转义 let objE = document.createElement("div"); objE.innerHTML = html; type ? (escape ? (html.includes('&') ? (objE.innerText = html) : (objE.innerText = new Option(html).innerHTML)) : (html.includes('<') && (objE.innerText = html))) : (objE.innerHTML = objE.innerText); return objE.innerText; }
-
错误处理
//监听除 try catch 捕捉的错误 window.onerror = (...error)=>{ window.open("http://stackoverflow.com/search?q=[js] + "+error[0]); } //强制报错 var err = new Error("错误信息"); console.error(err); //单纯日志输出,不影响代码执行 throw err; //显示报错,导致后面代码不解析