# JavaScript笔记
# Flag
- 性能提速:debounce(防抖)、throttle(节流/限频) (opens new window)
- js实现 throttle 和 debounce (opens new window)
- 7分钟理解JS的节流、防抖及使用场景 (opens new window)
- JavaScript中高阶函数的魅力 (opens new window)
- 函数节流与函数防抖 (opens new window)
- JS进阶篇1---函数节流(throttle) (opens new window)
- js防止重复触发事件 (opens new window)
- JS中的call、apply、bind方法详解 (opens new window)
- js实现replaceAll方法 (opens new window)
- 如何在 JavaScript 中清空数组? (opens new window)
- 利用html5读取本地文本文件及图片文件 (opens new window)
- 探秘神奇的IntersectionObserver:释放网页性能的黑科技! (opens new window)
- 上传文件net::ERR_UPLOAD_FILE_CHANGED (opens new window)
- 改变世界的 17 个方程式 - 用 JavaScript 重写 (opens new window)
回调地狱
什么是回调地狱:通常以javaScript的执行顺序来编写代码,在执行异步代码时,无论以什么顺序简单的执行代码,通常情况会变成许多层级的回调函数堆积
解决方法: 1.放弃使用匿名函数,给所有的函数都命名,以名字的方式传递回调函数;2.代码简洁;3.模块儿化,将重复代码写入一个函数体内; 4.Promise (opens new window)
循环loop
for/i
多次遍历代码块forEach
遍历对象属性,不能中断循环(使用break
语句或使用return
语句)for/in
遍历对象属性,实际是为循环enumerable
对象而设计,不推荐用for/in
来循环一个数组for/of
可遍历Array
、String
、TypedArray
、Map
、Set
、DOM collections
、enumerable
、generators
,弥补了forEach
和for/in
循环的短板while
当指定条件为 true 时循环一段代码块do/while
当指定条件为 true 时循环一段代码块
# 异步协程
ES6 co/yield方案
- Generator 函数是协程在 ES6 的实现,可以交出函数的执行权,
function*
yield
是Generator关键字,异步操作需要暂停的地方(主动交出执行权暂停执行),都用yield语句注明next()
恢复执行yield*
移交执行权调用另一个协程
- co: co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。
ES7 async/await 方案
- async/await是es7的新标准,async函数就是将 Generator 函数的
*
替换成async,将yield替换成await,仅此而已。
# 正则表达式
exec
是正则表达式的方法,参数是字符串match
字符串的方法,参数是正则表达
- 当正则表达式无子表达式,并且定义为非全局匹配时:exec和match执行的结果是一样,均返回第一个匹配的字符串内容;
- 当正则表达式无子表达式,并且定义为全局匹配时:exec和match执行,做存在多处匹配内容,则match返回的是多个元素数组;
- 当正则表达式有子表示时,并且定义为非全局匹配:exec和match执行的结果是一样返回多个匹配内容数组;
- 当正则表达式有子表示时,并且定义为全局匹配:exec和match执行的结果不一样,此时match将忽略子表达式,只查找全匹配正则表达式并返回所有内容;
也就说,exec与全局是否定义无关系,而match则于全局相关联,当定义为非全局,两者执行结果相同; exec没有匹配返回null,匹配有子表达式返回匹配结果数组下标0值为所有表达式结果,其他下标为子表达式的匹配
var str = `test https://www.bajins.com`;
console.log(new RegExp("var servers = (.*)","ig").exec(str));
console.log(str.match(new RegExp("var servers = (.*)","ig")));
console.log(new RegExp("test(.*)","ig").exec(str));
console.log(str.match(new RegExp("test(.*)","ig")));
// 匹配多个${}
var reg = /(\${.*?\})/g;
var reg = /\$\{[^\}]+\}/g;
var str = "如何匹配到 ${A0111}${A0117} 现在好像只能匹配一个。";
str.match(reg);
# HTTP
如果在业务场景中需要请求后端并使用返回数据(理想状态是拿到返回数据后下面的代码才执行),并且在多个地方使用相同请求后端代码,
Ajax中如果使用同步那么有可能会导致不可达异常,如果使用异步请求就不能按时序拿到后端返回值(会跳过)再执行后面的代码,
解决方案
:应该在封装请求后端代码(异步)函数的参数上传入要在(回调匿名函数中)返回值处理后调用其他代码的函数, 这里使用了尾调用 (opens new window) 图解尾调用优化 (opens new window)
- flyio (opens new window)
- HTTP封装 (opens new window)
- XMLHttpRequest—必知必会 (opens new window)
- XMLHttpRequest封装源码 (opens new window)
- Fetch各浏览器支持情况 https://caniuse.com/?search=fetch (opens new window)
- Fetch标准 https://github.com/whatwg/fetch (opens new window)
- https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API (opens new window)
http,XMLHttpRequest,Ajax的关系
- http是浏览器和web服务器交换数据的协议,规范
- XMLHttpRequest是JavaScript的一个对象,是浏览器实现的一组api函数(方法),使用这些函数,浏览器再通过http协议请求和发送数据
- Ajax最核心的依赖是浏览器提供的XMLHttpRequest对象
- axios 是一个基于Promise实现版本,本质上也是对原生XMLHttpRequest的封装,符合最新的ES规范
- fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象,使用了ES6中的promise对象
# 标签默认事件
阻止其他事件触发
- 事件捕获其实有三种方式,事件冒泡只是其中的一种:
- (1)IE从里到外(inside→outside)的冒泡型事件。
- (2)Netscape4.0从外到里(outside→inside)的捕获型事件。
- (3)DOM事件流,先从外到里,再从里到外回到原点(outside→inside→outside)的事件捕获方法。
- 以下事件不冒泡:blur、focus、load、unload。
focus事件是在冒泡到document之后再返回到原来的元素上面才被触发
// 阻止事件冒泡
function stopPropagation(event){
//var e = event? event:window.event;
var e = window.event || event;
e.stopPropagation();
}
/* 使用该样式,会阻止事件的触发。鼠标只会显示为箭头样式 */
pointer-events: none;
/* 鼠标禁用样式,然后使用js阻止事件的触发 */
cursor: not-allowed;
href伪协议
javascript:
是一个伪协议,表示在触发默认动作时,执行一段JavaScript代码。
javascript:;
表示什么都不执行,点击时没有任何反应。#
点击后,页面默认上滚到页的顶部,οnclick="return false"
或event.returnValue=false;
防止上滚到页的顶部####
用2个到4个#
或#all
,一个无意义的标签指定,不做任何处理。javascript:void(0);
表示一个死链接,执行空事件
<a href="javascript:void(0);" onclick="test()">{{ row.name }}</a>
原生方式
- 取消事件 event.preventDefault (opens new window)
- 阻止事件的其他监听器和冒泡 event.stopImmediatePropagation (opens new window)
- 阻止冒泡 event.stopPropagation (opens new window)
<a href="https://www.bajins.com" onclick="test();return false;">{{ row.name }}</a>
<a href="https://www.bajins.com" onclick="return test();">{{ row.name }}</a>
<script>
function test(){
return false;
}
</script>
<a href="https://www.bajins.com" onclick="test();return false;">{{ row.name }}</a>
<script>
function test(event){
event = window.event || arguments.callee.caller.arguments[0] || event;
event.returnValue = false : event.preventDefault();
}
</script>
VUE阻止默认行为
<a href="https://www.bajins.com" v-on:click.prevent="test()">{{ row.name }}</a>
<a href="https://www.bajins.com" v-on:click="test($event)">{{ row.name }}</a>
<script>
function test(event){
event.preventDefault();
}
</script>
# Storage和Cache
- 使用Chrome DevTools查看和编辑本地存储 (opens new window)
- https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Storage_API (opens new window)
- https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API (opens new window)
- HTML5 - 应用程序缓存(Application Cache) (opens new window)
sessionStorage.setItem("k", "v");
var value = sessionStorage.getItem("k");
console.log(value);
sessionStorage.removeItem("k");
sessionStorage.clear();
// 跟上面的sessionStorage有一样的方法
var value = localStorage.setItem('键',"值");
for(var i=0, len=localStorage.length; i<len; i++){
var key = localStorage.key(i);
var value = localStorage.getItem(key);
console.log(key + "=" + value);
}
/**
* 把水平滚动条位置和垂直滚动条位置保存在Cookie中
*/
function setScrollToCookie() {
var scrollTop, scrollLeft;
if (typeof window.pageYOffset != 'undefined') {
scrollTop = window.pageYOffset;
scrollLeft = window.pageXOffset;
} else if (typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat') {
scrollTop = document.documentElement.scrollTop;
scrollLeft = document.documentElement.scrollLeft;
} else if (typeof document.body != 'undefined') {
scrollTop = document.body.scrollTop;
scrollLeft = document.body.scrollLeft;
}
var date = new Date();
date.setHours(date.getHours() + 1); // 设置cookie的有效期
// 创建cookie,保存水平滚动条位置
document.cookie = "scrollTop=" + escape(scrollTop) + "; expires=" + date.toGMTString();
// 创建cookie,保存垂直滚动条位置
document.cookie = "scrollLeft=" + escape(scrollLeft) + "; expires=" + date.toGMTString();
}
/**
* 获取Cookie中存储的信息
*
* @param {Stirng} sName
*/
function getCookie(sName) {
var arr = document.cookie.match(/(scrollTop|scrollLeft)=([^;]+)(;|$)/);
if (arr != null) {
var aCookie = document.cookie.split("; "); // 将cookie中的数据切割成数组,方便遍历
for (var i = 0; i < aCookie.length; i++) { // 遍历cookie中的数据
var aCrumb = aCookie[i].split("="); // 将键和值分开
if (sName == aCrumb[0]) { // 判断是否是指定的键
return unescape(aCrumb[1]);
}
}
}
return null;
}
/**
* 加载页面时自动执行获取cookie保存值的方法
*/
window.onload = function () {
document.documentElement.scrollLeft = getCookie("scrollLeft");
document.body.scrollLeft = getCookie("scrollLeft"); // 获取水平滚动条位置
document.documentElement.scrollTop = getCookie("scrollTop");
document.body.scrollTop = getCookie("scrollTop"); // 获取垂直滚动条位置
}
window.onunload = setScrollToCookie();
window.onbeforeunload = setScrollToCookie();
jQuery数据缓存方案
使用隐藏控件或者js全局变量来临时存储数据,全局变量容易导致命名污染,隐藏控件导致经常读写dom浪费性能。jQuery提供了数据缓存方案
同window全局变量或标签元素属性一样只针对于当前页面有效,跳转链接后将清除
$.data()
用于在指定的元素上存取临时数据,页面刷新数据都将被移除$.attr()
绑定数据在标签的属性上,一定要以data-
开头
// hasData用来判断HTMLElement或JS对象是否具有数据
console.log($("#a").hasData('name'));// false
// 添加数据,值存在`$.cache`中,key使用`$.expando`生成
$("#a").data('name', '11111111');
// 标签上`data-`开头属性也是数据
$("#a").attr("data-name", '2222222222');
// 读取数据,在$.cache中找,没有则去标签的`data-`开头属性中查找
console.log($("#a").data('name'));
// 删除数据,不能删除标签上`data-`开头属性的数据
$("#a").removeData('name');
$("#a").removeAttr('data-name');
console.log($("#a").data('name'));//undefined
# 自动触发事件
var event = document.createEvent('Event'); // 创建
event.initEvent('keydown', true, false); // 初始化
event = Object.assign(event, {
ctrlKey: false,
metaKey: false,
altKey: false,
which: 13,
keyCode: 13, // 回车
key: 'Enter',
code: 'Enter'
});
var inp = document.querySelector('.input');
inp.value = new Date().toLocaleString();
inp.dispatchEvent(event); // 触发
inp.detachEvent(event); // 事件删除
var e = $.Event("keydown") || jQuery.Event("keydown"); // 创建事件
e.keyCode = 13; // 回车
$("input").trigger(e); // 触发事件
# 合并对象
Object.assing({}, obj);
{...{}, ...obj}
// Jquery
$.extend({}, obj)
# 选择器特殊符转义
function escapeSelector(src) {
// javascript正则表达式中的特殊字符
const jsSpecialChars = ["\\", "^", "$", "*", "?", ".", "+", "(", ")", "[", "]", "|", "{", "}"];
for (const sc of jsSpecialChars) {
src = src.replace(new RegExp("\\" + sc, "g"), "\\" + sc);
}
// jquery中的特殊字符,不是正则表达式中的特殊字符
const jquerySpecialChars = ["~", "`", "@", "#", "%", "&", "=", "'", "\"", ":", ";", "<", ">", ",", "/"];
for (const sc of jquerySpecialChars) {
src = src.replace(new RegExp(sc, "g"), "\\" + sc);
}
return src;
}