技术清单
[[toc]]
js正则删除行内块之间的空格 let rep = function (match, item1, item2, item3 ) { return item1 + item3 } let source = template.content.replace(/(>)(\s*)(<)/g , rep);
本地缓存 localStorage的库
JS检测本地储存localStorage的变化
window .addEventListener('storage' , function (event ) { console .log(event) })
IndexedDB的库
页面直接编辑 document .body.contentEditable = true
// 一些API的简单使用 <input type =button value =剪切 οnclick =document.execCommand( 'Cut ')> <input type =button value =拷贝 οnclick =document.execCommand( 'Copy ')> <input type =button value =粘贴 οnclick =document.execCommand( 'Paste ')> <input type =button value =撤消 οnclick =document.execCommand( 'Undo ')> <input type =button value =重做 οnclick =document.execCommand( 'Redo ')> <input type =button value =删除 οnclick =document.execCommand( 'Delete ')> <input type =button value =黑体 οnclick =document.execCommand( 'Bold ')> <input type =button value =斜体 οnclick =document.execCommand( 'Italic ')> <input type =button value =下划线 οnclick =document.execCommand( 'Underline ')> <input type =button value =停止 οnclick =document.execCommand( 'stop ')> <input type =button value =保存 οnclick =document.execCommand( 'SaveAs ')> <input type =button value =另存为 οnclick =document.execCommand( 'Saveas ',false ,'c: \\test.htm ')> <input type =button value =字体 οnclick =document.execCommand( 'FontName ',false ,fn )> <input type =button value =字体大小 οnclick =document.execCommand( 'FontSize ',false ,fs )> <input type =button value =刷新 οnclick =document.execCommand( 'refresh ',false ,0 )>
function jiacu ( ) { document .execCommand("Bold" ,false ,null ); } function xieti ( ) { document .execCommand("Italic" ,false ,null ); } function xiahua ( ) { document .execCommand("Underline" ,false ,null ); } function shanchu ( ) { document .execCommand("StrikeThrough" ,false ,null ); } function setFontName (param ) { document .execCommand("FontName" ,false ,param); document .getElementById("fontUl" ).style.display="none" ; } function setFontSize (param ) { document .execCommand("FontSize" ,false ,param); document .getElementById("sizeUl" ).style.display="none" ; } function setFontColor (param ) { document .execCommand("ForeColor" ,false ,param) document .getElementById("fontColor1" ).style.display="none" ; } function setBackColor (param ) { document .execCommand("BackColor" ,false ,param) document .getElementById("bgColor1" ).style.display="none" ; } function removeFormat ( ) { document .execCommand("RemoveFormat" ,false ,null ); } function duiqiway (param ) { document .execCommand(param,false ,null ); document .getElementById("duiqiUl" ).style.display="none" ; } function insertList (param ) { document .execCommand(param,false ,null ); alert("暂时未实现" ); document .getElementById("liebiaoUl" ).style.display="none" ; } function changeIndent (param ) { document .execCommand(param,false ,null ); alert("暂时未实现" ); } function setLink (param ) { document .execCommand(param,false ,"http://blog.csdn.net/u011043843" ); alert("暂时未实现" ); } function insertBQ (param ) { document .execCommand("InsertImage" ,false ,param); document .getElementById("bqDiv" ).style.display="none" ; } function parag (param ) { document .execCommand("formatBlock" ,false ,param); document .getElementById("paraUl" ).style.display="none" ; }
实现一套撤销重恢复做功能 上面的方案仅仅针对文字类需求有效,而且兼容性比较低,一些设备都不能用。
如果是画布的话,只能自己实现一套
我们需要两个栈来实现这个操作:分别是撤销栈和回退栈。
整体流程: 1.每当我们开始拖拽画布元素,或者开始缩放之前,我们要保存此次操作时刻的屏幕快照,在本系统中,组件都以json数组的形式来渲染。所以我们需要保存的就是当前画布的组件json数组。.将当前画布的json数组作为一个整体push进撤销栈,表示记录本次操作。
2.我们拖动元素这个动作结束了,当前画布的json数组必然发生了更新。现在我们想撤销这次拖动操作。点击撤销后,先将当前画布的json数组push到回退栈中,用撤销栈中最后一个json数组来setstate重新渲染画布元素(第一步push的那个json数组)。再使用数组pop方法,删除撤销栈中最后一个json数组。
3.回退时先将当前画布的json数组push到撤销栈中,用回退栈中最后一个json数组来setstate重新渲染画布元素(第二步push的那个json数组)。再使用数组pop方法,删除回退栈中最后一个json数组。
看下控制台输出:
部分实现逻辑
import { cloneDeep } from 'lodash' ;import { useModel } from 'umi' ; const maxStep = 60 ; const undoQueue: any = [];let redoQueue: any = []; const QueueManager = () => { const { components, setComponents } = useModel('visualPage' ); const saveSnap = () => { const snap = cloneDeep(components); if (undoQueue.length >= maxStep) { undoQueue.shift(); } undoQueue.push(snap); redoQueue = []; }; const undo = () => { const snap = cloneDeep(components); const c = cloneDeep(undoQueue[undoQueue.length - 1 ]); setComponents(c); redoQueue.push(snap); undoQueue.pop(); }; const redo = () => { const snap = cloneDeep(components); const c = cloneDeep(redoQueue[redoQueue.length - 1 ]); setComponents(c); undoQueue.push(snap); redoQueue.pop(); }; return { saveSnap, undo, redo, undoQueue, redoQueue, }; }; export default QueueManager;
双向数据绑定 defineProperty
版本
<input type="text" id="input" > <span id="text" /> const data = {};Object .defineProperty(data, 'text' , { set (value) { this .value = value; input.value = value; text.innerText = value; return value }, get (val) { console .log('获取值' , this ); return val } }); input.oninput = function (e ) { console .log(data.text = e.target.value); console .log(data.text); };
proxy
版本
<input type="text" id="input" > <span id="text" /> const data = {};const handler = { set (target, key, value) { target[key] = value; input.value = value; text.innerText = value; return value }, get (obj, value, target) { console .log('获取值' ) return value } }; const proxy = new Proxy (data, handler);input.oninput = function (e ) { proxy.text1 = e.target.value console .log(proxy.text2 = e.target.value); console .log(proxy.text1); };
节流防抖 export const debounce = function (func, delay ) { let timer; return function (...args ) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { func.apply(this , args) }, delay) } }; export const throttle = function (func, delay ) { let now = Date .now(); return function (...args ) { const current = Date .now(); if (current - now >= delay) { func.apply(this , args); now = current } } }; methods:{ hangleChange:debounce(function (e ) { console .log(this .value) }) } created(){ this .fnScroll = this .throttle(fn, 1000 ) },
导出表格 const url = window .URL.createObjectURL( new Blob([res], { type : "application/vnd.ms-excel" }) ); const fileName = `操作日志${dayjs().format("YYYY-MM-DD" )} .xls` ;let link = document .createElement("a" );link.style.display = "none" ; link.href = url; link.setAttribute("download" , fileName); document .body.appendChild(link);link.click(); window .URL.revokeObjectURL(url);document .body.removeChild(link);
window.requestAnimationFrame & window.requestIdleCallback 软知识:
屏幕刷新频率: 屏幕每秒出现图像的次数,普通笔记本为60Hz.
动画原理: 计算机每16.7ms刷新一次(1000/60),由于人眼的视觉停留,所以看起来是流畅的移动。
setTimeout: 通过设定间隔时间
来不断改变图像位置,达到动画效果。但是容易出现卡顿、抖动的现象;原因是:
1、settimeout任务被放入异步队列
,只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚; 2、settimeout的固定时间间隔不一定与屏幕刷新时间相同
,会引起丢帧。
requestAnimationFrame: 优势:
1.由系统决定回调函数的执行时机
,60Hz的刷新频率,那么每次刷新的间隔中会执行一次回调函数,不会引起丢帧,不会卡顿 2.CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时
,setTimeout 仍然在后台执行动画任务
,由于此时页面处于不可见或不可用状态, 刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停, 因此跟着系统步伐走的requestAnimationFrame也会停止渲染
,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。 3.函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行, 使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。
requestAnimationFrame const goTop=() => {let product_content = document .getElementById('product_content' );this .progress = product_content.scrollTop; if (this .progress >= 0 ) { window .requestAnimationFrame(this .step); } } export const step= () => {let product_content = document .getElementById('product_content' ); if (this .progress > 0 ) { this .progress -= 250 ; product_content.scrollTop = this .progress; window .requestAnimationFrame(this .step); }else { window .cancelAnimationFrame(this .step); } }
requestIdleCallback 浏览器每一帧都需要完成哪些工作?
通过上图可看到,一帧内需要完成如下六个步骤的任务:
处理用户的交互 JS 解析执行 帧开始。窗口尺寸变更,页面滚去等的处理 requestAnimationFrame(rAF) 布局 绘制
上面六个步骤完成后没超过16.7ms,说明时间有富余,此时就会执行requestIdleCallback里注册的任务
( requestIdleCallback的时长并不是16ms,他是一个肉眼觉察不到的时间) ,如果没rAF这样的循环处理,浏览器一直处于空闲状态的话,deadline.timeRemaining
可以得到的最长时间
在空闲时段
这种情况下,用户代理可能没有即将完成的任务,可以限制空闲周期的结束。为了避免在不可预测的任务(例如用户输入的处理)中引起用户可察觉的延迟,这些空闲周期的长度应限制为最大值50ms。
最大期限为50毫秒,是根据研究[ RESPONSETIME ] 得出的,该研究表明,对用户输入的100毫秒以内的响应通常被认为对人类是瞬时的。将闲置截止期限设置为50ms意味着即使在闲置任务开始后立即发生用户输入,用户代理仍然有剩余的50ms可以在其中响应用户输入而不会产生用户可察觉的滞后。
API
var handle = window .requestIdleCallback(callback[, options])
callback:回调,即空闲时需要执行的任务,该回调函数接收一个IdleDeadline对象作为入参。其中IdleDeadline对象包含:
didTimeout ,布尔值,表示任务是否超时,结合 timeRemaining 使用。
timeRemaining() ,表示当前帧剩余的时间,也可理解为留给任务的时间还有多少。
options:目前 options 只有一个参数
timeout 。表示超过这个时间后,如果任务还没执行,则强制执行,不必等待空闲
requestIdleCallback(myNonEssentialWork, { timeout : 2000 }); const tasks = [ () => { console .log("第一个任务" ); }, () => { console .log("第二个任务" ); }, () => { console .log("第三个任务" ); }, ]; function myNonEssentialWork (deadline ) { while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0 ) { work(); } if (tasks.length > 0 ) requestIdleCallback(myNonEssentialWork); } function work ( ) { tasks.shift()(); console .log('执行任务' ); }
超时的情况,其实就是浏览器很忙,没有空闲时间,此时会等待指定的 timeout 那么久再执行 ,通过入参 dealine 拿到的 didTmieout 会为 true,同时 timeRemaining () 返回的也是 0
。 超时的情况下如果选择继续执行的话,肯定会出现卡顿的,因为必然会将一帧的时间拉长
cancelIdleCallback
与 setTimeout 类似,返回一个唯一 id,可通过 cancelIdleCallback 来取消任务。
MutationObserver MutationObserver 是一个可以监听DOM结构变化的接口。
异步的 (微任务)
childList:如果突变目标的子代被观察,则设置为 true。
attributes:如果要观察目标属性的突变,则设置为 true。 如果指定了 attributeOldValue 和(或)attributeFilter,则可以省略。
characterData:如果要观察到目标数据的突变,则设置为 true。 如果指定了characterDataOldValue,则可以省略。
subtree:如果突变不仅仅是目标对象,而且包括目标的后代(descendants),则设置为 true。
attributeOldValue:如果前面的 attributes 属性设置为 true 或省略,并且目标的属性值在突变前要做记录,则设置为 true。
characterDataOldValue:如果前面的 characterData 属性设置为 true 或省略,并且目标的数据在突变前要做记录,则设置为 true。
attributeFilter:设置为属性本地名称列表(没有命名空间),如果不是所有属性突变都需要观察,属性为 true 或省略。例如:[‘class’,’src’]
new MutationObserver(() => { let dom = document .body.getAttribute('data-random' ) console .log(dom) }).observe(document .body, { attributes: true }) document .body.setAttribute('data-random' , Math .random())document .body.setAttribute('data-random' , Math .random())document .body.setAttribute('data-random' , Math .random())
ResizeObserver ResizeObserver 创建一个新的ResizeObserver对象监听元素大小变化
这个接口可以监听到元素的变化,以前我们只能通过window.resize来监听页面变化,现在有了这个API任何元素都可以监听
<!--html--> <textarea id="main" ></textarea> / / javascript let mainEl = document.querySelector('#main') const resizeObserver = new ResizeObserver(entries => { console.log(entries) }) / /拉动textarea的大小,即可看到输出 / / 监听元素大小改变 resizeObserver.observe(mainEl) / /ResizeObserverEntry [{target: textarea#main, contentRect: {x: 2, y: 2, width: 143, height: 43, top: 2, …}}] / / 取消某个元素监听 / /resizeObserver.unobserve(mainEl) / / 取消全部元素监听 / /resizeObserver.disconnect()
IntersectionObserver 是否可见 语法: var observer = new IntersectionObserver(callback[, options]);
callback
当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:
entries : 一个IntersectionObserverEntry对象的数组,每个被触发的阈值,都或多或少与指定阈值有偏差。
observer : 被调用的IntersectionObserver实例。
options 可选
一个可以用来配置 observer 实例的对象。如果options未指定,observer 实例默认使用文档视口作为 root,并且没有 margin,阈值为 0%(意味着即使一像素的改变都会触发回调函数)。你可以指定以下配置:
root
监听元素的祖先元素Element对象,其边界盒将被视作视口。目标在根的可见区域的的任何不可见部分都会被视为不可见。
rootMargin
一个在计算交叉值时添加至根的边界盒 (bounding_box (en-US)) 中的一组偏移量,类型为字符串 (string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和 CSS 中的margin 属性等同; 可以参考 intersection root 和 root margin 来深入了解 margin 的工作原理及其语法。默认值是”0px 0px 0px 0px”。
threshold
规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。若指定值为 0.0,则意味着监听元素即使与根有 1 像素交叉,此元素也会被视为可见。若指定值为 1.0,则意味着整个元素都在可见范围内时才算可见。可以参考阈值来深入了解阈值是如何使用的。阈值的默认值为 0.0。
实践: vue3 实现一个图片懒加载
import { DirectiveBinding } from 'vue' ;const loadingImg = 'xxx' ;const defaultImg = 'xxx' ;const imageAsync = (url: string ) => { return new Promise <void >((resolve, reject ) => { const img = new Image( ); img.src = url; img.onload = ( ) => { resolve( ); }; img.onerror = (err ) => { reject(err ); }; } );}; export default { // @ts -ignore mounted (el, binding: DirectiveBinding ) { el .src = loadingImg ; // https ://developer .mozilla .org /zh -CN /docs /Web /API /IntersectionObserver /IntersectionObserver #threshold const io = new IntersectionObserver ( (entries ) => { entries.forEach((item ) => { if (item.isIntersecting ) { imageAsync(binding.value ) .then(( ) => { el.src = binding.value; } ) .catch ((error ) => { console .log(error ); el.src = defaultImg; } ); io.unobserve(item.target ); } } ); }, { rootMargin: '0px 0px 10px 0px', threshold: 0, } ); io .observe (el ); }, };
Es6模块导入 import * as xxx from ‘xxx’: import {a,b,c,...} from ‘xxx’import xxx from ‘xxx’:(export default xxx)export {default as docs} from "./xxx"
Tay Catch try { value = this .getter.call(vm, vm) } catch (e) { handleError(e, vm, `getter for watcher "${this .expression} "` ) } finally { console .log('失败与否必须执行' ) }
await 执行顺序 await 后面是 Promise 的时候,会阻断后面的执行,变成同步执行,不然的话此时添加 await 没有效果
const copyTask = async () => { await new Promise ((resolve, reject ) => { setTimeout(() => { console .log(1111 ); resolve(3333 ); }, 2000 ); }) await copyParentTask(params); await getProjectTasksList(); console .log(2222 ); return true ; }; const copyTask = async () => { await setTimeout(() => { console .log(1111 ); }, 2000 ); await copyParentTask(params); await getProjectTasksList(); console .log(2222 ); return true ; };
控制进度 (任务编排) class Deferred { constructor () { this .promise = new Promise ((resolve, reject ) => { this .resolve = resolve; this .reject = reject; }); } } let frameworkStartedDefer = new Deferred()async function ff ( ) { await frameworkStartedDefer.promise; console .log(121212 ) } ff(); frameworkStartedDefer.resolve()
睡觉函数 function sleep (ms ) { return new Promise (resolve => setTimeout(resolve, ms)); } async function ff ( ) { await sleep(3000 ); console .log(121212 ) } ff();
弱网检测 const isSlowNetwork = navigator.connection ? navigator.connection.saveData || (navigator.connection.type !== 'wifi' && navigator.connection.type !== 'ethernet' && /(2 |3 )g/.test(navigator.connection.effectiveType)) : false ; if (!navigator.onLine || isSlowNetwork) { return ; }
获取 某个 Attr 属性上的 dom <div class ="report-position" data-log-expo ="{" event" :" get_sharebooks_content" }" > </div >
获取 dom
document .querySelectorAll(`[data-log-expo]` )
userAgent var browser = { userAgent: function ( ) { var ua = navigator.userAgent; var ualower = navigator.userAgent.toLocaleLowerCase(); return { trident: ua.indexOf('Trident' ) > -1 , presto: ua.indexOf('Presto' ) > -1 , webKit: ua.indexOf('AppleWebKit' ) > -1 , gecko: ua.indexOf('Gecko' ) > -1 && ua.indexOf('KHTML' ) == -1 , mobile: !!ua.match(/AppleWebKit.*Mobile.*/ ) || !!ua.match(/AppleWebKit/ ), ios: !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/ ), android: ua.indexOf('Android' ) > -1 , iPhone: ua.indexOf('iPhone' ) > -1 , iPad: ua.indexOf('iPad' ) > -1 , webApp: ua.indexOf('Safari' ) == -1 , QQbrw: ua.indexOf('MQQBrowser' ) > -1 , weiXin: ua.indexOf('MicroMessenger' ) > -1 , QQ: ualower.match(/\sQQ/i ) == " qq" , weiBo: ualower.match(/WeiBo/i ) == "weibo" , ucLowEnd: ua.indexOf('UCWEB7.' ) > -1 , ucSpecial: ua.indexOf('rv:1.2.3.4' ) > -1 , webview: !(ua.match(/Chrome\/([\d.]+)/ ) || ua.match(/CriOS\/([\d.]+)/ )) && ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/ ), ucweb: function ( ) { try { return parseFloat (ua.match(/ucweb\d+\.\d+/gi ).toString().match(/\d+\.\d+/ ).toString()) >= 8.2 } catch (e) { if (ua.indexOf('UC' ) > -1 ) { return true ; } return false ; } }(), Symbian: ua.indexOf('Symbian' ) > -1 , ucSB: ua.indexOf('Firofox/1.' ) > -1 }; }() };
import和require区别 console .log(1 )import {sum} from "./sum" console .log(sum(1 ,2 ))console .log(2 )export const sum=(a,b )=> a+b;
检测数据类型 typeof number,boolean,string,function (函数),symbol ,object (NULL ,数组,对象),undefined 。
instanceof [1 , 2 , 3 ] instanceof Array ; [1 , 2 , 3 ] instanceof Object ; function myInstanceof (val, type ) { let rightProto = type.prototype; let leftPrevProto = val.__proto__; while (true ) { if (leftPrevProto === null ) { return false ; } if (leftPrevProto === rightProto) { return true ; } leftPrevProto = leftPrevProto.__proto__ ; } }
constructor 1:null 和 undefined 无constructor,这种方法判断不了。
2:还有,如果自定义对象,开发者重写prototype之后,原有的constructor会丢失,因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。
'' .constructor===String new Number (111 ).constructor===Number (11 ).constructor===Number false .constructor===Boolean new Date ().constructor===Date new Function ().constructor===Function [].constructor===Array class Chameleon { constructor ({ newColor = "green" } = {}) { this .newColor = newColor; } } Chameleon.constructor===Function new Chameleon().constructor.constructor===Function Object .prototype.toString.call(new Chameleon().constructor) new ew Chameleon().constructor===Function
Object.prototype.toString.call() function is (type, obj ) { var clas = Object .prototype.toString.call(Object (obj)).slice(8 , -1 ); return obj !== undefined && obj !== null && clas === type; } is('String' , 'test' ); is('String' , new String ('test' ));
HTML页面加载完毕后运行JS window .onload=function ( ) {} $(function ( ) {}); $(document ).ready(function ( ) {}) <body onload="fn()" ></body> / / 最晚执行
两者的主要区别
当一个文档完全下载到浏览器中时,才会触发window.onload
事件
$(document).ready{ }是在DOM完全就绪并可以使用时调用,此时可能图片等还可能没有下载完成
解释 script 标签的加载和执行顺序 在没有使用 defer 或 async 属性的情况下,<script> 标签是按它们在 HTML 文档中出现的顺序从上到下依次加载和执行的。这个过程是同步的,即在一个脚本加载和执行完成之前,浏览器不会继续解析和渲染后续的 HTML 内容。
默认行为(不带 defer 或 async)
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Document</title > <script src ="script1.js" > </script > <script src ="script2.js" > </script > </head > <body > <h1 > Hello, World!</h1 > </body > </html >
在这个示例中:
浏览器加载 script1.js,并执行其中的代码。 在 script1.js 加载和执行完成之前,浏览器不会加载或执行 script2.js,也不会继续解析 HTML。 加载并执行完 script1.js 后,浏览器才会加载和执行 script2.js。 完成所有脚本的加载和执行后,浏览器继续解析剩余的 HTML。
使用 defer 带有 defer 属性的 <script> 标签会按顺序异步加载,但在文档解析完成后才会按顺序执行。
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Document</title > <script src ="script1.js" defer > </script > <script src ="script2.js" defer > </script > </head > <body > <h1 > Hello, World!</h1 > </body > </html >
在这个示例中:
script1.js 和 script2.js 会同时异步加载,不会阻塞 HTML 的解析。 HTML 文档解析完成后,脚本会按照它们在文档中的顺序执行。
使用 async 带有 async 属性的 <script> 标签会异步加载和执行,不保证执行顺序。
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Document</title > <script src ="script1.js" async > </script > <script src ="script2.js" async > </script > </head > <body > <h1 > Hello, World!</h1 > </body > </html >
在这个示例中:
script1.js 和 script2.js 会同时异步加载,不会阻塞 HTML 的解析。 每个脚本在加载完成后立即执行,执行顺序不一定与它们在文档中的顺序一致。
总结 默认(无 defer 或 async):按顺序加载和执行,阻塞 HTML 解析。 defer:按顺序异步加载,文档解析完成后按顺序执行。 async:异步加载,加载完成后立即执行,不保证执行顺序
JS 动态加载 JS function reloadJSFn (id, newJS ) { let oldjs = document .getElementById(id); if (document .getElementById(id)) oldjs.parentNode.removeChild(oldjs); let scriptObj = document .createElement('script' ); scriptObj.src = newJS; scriptObj.type = 'text/javascript' ; scriptObj.id = id; document .getElementsByTagName('head' )[0 ].appendChild(scriptObj); } fetch('https://gcore.jsdelivr.net/gh/hzfvictory/cdn@master/water-mark/index.js' ).then(e => e.text()).then((res ) => { (0 , eval )(res); waterMark('hezhenfeng' ,null ,'app' ) })
Symbol const Age=Symbol ();typeof Age === 'symbol'
let obj = { [Symbol ('name' )]: '一斤代码' , age: 18 , title: 'Engineer' } Object .keys(obj) for (let p in obj) { console .log(p) } Object .getOwnPropertyNames(obj) JSON .stringify(obj) Object .getOwnPropertySymbols(obj) Reflect .ownKeys(obj)
const TYPE_AUDIO = Symbol ()const TYPE_VIDEO = Symbol ()const TYPE_IMAGE = Symbol ()
小东西
const pkg = require ('../package.json' );window .mmPlayer = window .mmplayer = `欢迎使用 当前版本为:V${pkg.version} 作者:${pkg.auter} ` console .info(`%c${window .mmPlayer} ` , `color:blue` );
console .time(111 )Array (1000000 ).keys()console .timeEnd(111 )
清空控制台历史记录
在控制台右键
,或者按下 Ctrl 并单击鼠标
,选择 Clear Console。
在脚本窗口输入 clear()
执行。
使用快捷键 command + K
切换主题
Chrome 提供了 亮&暗 两种主题,当你视觉疲劳的时候,可以 switch 哦, 快捷键 command+shift+p
,打开 Command Menu,输入 theme
,即可选择切换
Think 循环 <if condition="$Think.get.edit == 1 OR $Think.get.judge == 1" >disabled</if > <?php if ($_GET['id' ] == $parter['id' ]){ echo "selected" ;}?> <?php dump ($vo)?> <volist name="areas" id="vo" ></volist>
js文件压缩原因和压缩原理 压缩: 删除 Javascript 代码中所有注释、跳格符号、换行符号及无用的空格,从而压缩 JS 文件大小。混淆: 经过编码将变量和函数原命名改为毫无意义的命名,以防止他人窥视和窃取 Javascript 源代码。
javascript文件压缩的原理
第一个当然就是去掉注释了。
另外就是跟CSS压缩相同的去掉换行符,空格什么的。
JAVASCRIPT中有几种变量形式,如变量,函数名,函数的参数等,通常我们在手写JS代码的时候,为了便于理解,我们都会给这些变量名以直观易懂的字符串,如:var title=”javascript”;这个习惯是值得推崇的。
但是,这些变量对于用户理解有帮助,对于计算机却没什么影响,如果我们把前面的句子变成:var a=”javascript”;对电脑来讲是一样的。
通常深度压缩JS都必须要做的一步就是尽量地缩短变量名,因为一份体积巨大的JS代码,其中的变量名会占去不少空间。
26个单字母,几乎就可以把一个函数中所有的参数都写完,所以我们经常在压缩版的JS代码中发现a,b,c,d之类的连续变量。
另外,Javascript有个特性就是不同作用域的变量名可以任意重复,所以此函数中有a,b,c,d,其他函数也可以有。这样短又大量重复的变量可以让人索云里雾里不知所云,也变相的起到了加密JS代码的作用.
注意
压缩前的代码格式要标准。因为去掉换行与空格时,所有语句就变成一行了,如果你的代码有瑕疵(比如某行少了个分号),那就会导致整个文件报错。当然,现在有的压缩工具已经比较智能了。
备份原文件
压缩很可能不会一次成功,一般要多试,多改
允许网页访问某些函数来测量网页和Web应用程序的性能,包括 Navigation Timing API和高分辨率时间数据
function getPerformanceTiming ( ) { var performance = window .performance; if (!performance) { console .log('你的浏览器不支持 performance 接口' ); return ; } var t = performance.timing; var times = {}; times.loadPage = t.loadEventEnd - t.navigationStart; times.domReady = t.domComplete - t.responseEnd; times.redirect = t.redirectEnd - t.redirectStart; times.lookupDomain = t.domainLookupEnd - t.domainLookupStart; times.ttfb = t.responseStart - t.navigationStart; times.request = t.responseEnd - t.requestStart; times.loadEvent = t.loadEventEnd - t.loadEventStart; times.appcache = t.domainLookupStart - t.fetchStart; times.unloadEvent = t.unloadEventEnd - t.unloadEventStart; times.connect = t.connectEnd - t.connectStart; return times; }
计算性能指标
可以使用Navigation.timing 统计到的时间数据来计算一些页面性能指标,比如DNS查询耗时、白屏时间、domready等等。如下:
DNS查询耗时 = domainLookupEnd - domainLookupStart
TCP链接耗时 = connectEnd - connectStart
request请求耗时 = responseEnd - responseStart
解析dom树耗时 = domComplete - domInteractive
白屏时间 = domloadng - fetchStart
domready时间 = domContentLoadedEventEnd - fetchStart
onload时间 = loadEventEnd - fetchStart
读取注释 function eachComment (ele ) { let child = ele.childNodes[0 ]; if (child.nodeType === 8 ) { console .log(child.nodeValue); } } let bodyElement = document .getElementsByTagName("body" )[0 ];eachComment(bodyElement);
参看文档 github
博客1
博客2
博客3