函数防抖与函数节流
对于某些 高频触发 的事件,如果事件处理函数的调用频率没有限制的话,那么将会大大加重浏览器的负担
这时我们可以采用防抖函数或节流函数,减少事件处理函数的调用频率,同时保证不会影响用户体验
防抖函数
场景:在触发事件 n 秒后,才会执行事件处理函数,如果 n 秒内再次触发,那么重新计时
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Debounce</title>
<script>
function debounce(handler, delay) { // 防抖函数
var timer = null
return function () {
var that = this
var args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(function() { handler.apply(that, args) }, delay)
}
}
function show() { // 代理 log 方法
console.log.apply(console, arguments)
}
let debounce_show = debounce(show, 1000)
</script>
</head>
<body>
<button onclick="debounce_show('Hello', 'World')">Debounce</button>
</body>
</html>
节流函数
防抖函数有一个问题,假如这个事件一直触发,那么这个事件的处理函数将永远无法执行
而使用节流函数可以解决这个问题,当持续触发事件时,节流函数可以保证一定时间内调用一次处理函数
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Debounce</title>
<script>
function throttle(handler, waitTime) { // 节流函数
var timer = null
var lastTime = Date.now()
return function () {
var that = this
var args = arguments
var currTime = Date.now()
var remaining = waitTime - (currTime - lastTime)
if (timer) clearTimeout(timer)
if (remaining <= 0) {
handler.apply(that, args)
lastTime = Date.now()
} else {
timer = setTimeout(function() { handler.apply(that, args) }, waitTime)
}
}
}
function show() { // 代理 log 方法
console.log.apply(console, arguments)
}
let throttle_show = throttle(show, 1000)
</script>
</head>
<body>
<button onclick="throttle_show('Hello', 'World')">Throttle</button>
</body>
</html>
深拷贝和浅拷贝的理解
简单点来说,就是假设 B 变量复制了 A 变量,当修改 A 变量时,看 B 变量是否会发生变化,如果 B 变量也跟着变了,说明这是浅拷贝,拿人手短,如果 B 变量没变,那就是深拷贝,自食其力。
var a = 'aaa';
var b = a;
a = 'aaaaa';
console.log(a, b); // a = aaaaa,b = aaa 深拷贝
var a = { name: 'aaa', age: 12 };
var b = a;
a.age = 10;
console.log(a, b); // a = { name: 'aaa', age: 10 },b = { name: 'aaa', age: 10 } 浅拷贝
引用数据类型浅拷贝
对于基本数据类型,名字和值都会存储在栈内存中,并不存在浅拷贝的情况,但如果是引用数据类型,名字存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值。
- 当 b = a 进行拷贝时,其实复制的是 a 的引用地址,而并非堆里面的值。
- 当 a.age = 10 时进行数组修改时,由于 a 与 b 指向的是同一个地址,所以自然 b 也受了影响,这就是所谓的浅拷贝了
实现深拷贝的方法
在拷贝引用数据类型时,为了不影响拷贝对象,需要将对象浅拷贝转化为深拷贝模式,达到互不干扰
(1)对象深拷贝推荐使用 ES6 扩展运算符
var a = { name: 'aaa', age: 12 };
var b = { ...a, age: 10 };
console.log(a, b); // a = { name: 'aaa', age: 12 },b = { name: 'aaa', age: 10 }
--------------------------------------------------------------------------------------------------------------------------------
var a = { name: 'aaa', age: 12 };
var b = Object.assign({}, a);
b.age = 10;
console.log(a, b); // a = { name: 'aaa', age: 12 },b = { name: 'aaa', age: 10 }
(2)数组深拷贝推荐使用 concat 方法
var a = [1, 2, 3, 4];
var b = [].concat(a);
b[2] = 6;
console.log(a, b); // a = [1, 2, 3, 4],b = [1, 2, 6, 4]
传值方式
前端界面上的数据信息通常都是由后端提供的接口,通过查询数据库获取的,而有时候某个后端接口返回的数据可能在多个页面或板块中使用,那么就需要前端开发者进行传值处理,当然你重新去调用后端接口得到数据也可以呈现结果,若用户设备网络波动了一下,这样就没法取得数据,影响了用户体验,为了增强用户体验,强烈建议使用前端传值方式
路由传值
常用于页面之间进行值的传递,见于 get 请求,打开浏览器输入:www.baidu.com 在搜索输入框内输入 csdn 回车,查看浏览器地址栏信息发现
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=csdn&fenlei=256&rsv_pq=fe700cf10004535e&rsv_t=afbf6a%2BYix00Xyr79pftJRBUgD4IRXeWNmo0k%2F5JzzYVAEuasjD%2BbQrd0j8&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=7&rsv_sug1=8&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&inputT=5992&rsv_sug4=10935&rsv_sug=1
- https://www.baidu.com/ 百度的搜索地址
- https://www.baidu.com/s 百度提供的搜索接口
- ?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=csdn… 路由参数
路由传值的格式给接口后面添加个问号,并以 & 链接多个参数对,且每个参数对都是以 key_name=key_value 格式,路由取参也是通过 key_name 来获取对应的参数的参数值的:
- 首先需要通过 window.location.search 获取路由参数,
- 然后将路由参数进行编辑处理,最终转化为 JSON 对象
- 最后通过对象 .key_name 方式取得 key_name 参数值
var params = {};
var reg = new RegExp( "([^?=&]+)(=([^&]*))?", "g" );
window.location.search.replace(reg, ($0, $1, $2, $3) => params[$1] = $3);
console.log(params.wd); // 输出结果:csdn
按 F12 进入 Console 控制台,执行上述代码,打印的正是搜索内容:csdn,试着将搜索输入框内的 csdn 换成中文的 “前端” 回车,再在控制台执行上述语句,打印的却是中文加密码:%E5%89%8D%E7%AB%AF,这里就需要进行中文解码:
var params = {};
var reg = new RegExp( "([^?=&]+)(=([^&]*))?", "g" );
window.location.search.replace(reg, ($0, $1, $2, $3) => params[$1] = decodeURI($3));
console.log(params.wd); // 输出结果:前端
- decodeURI() 函数用于解码 JS 中的 URL,它将编码的 URL 字符串作为参数并返回已解码的字符串
- encodeURI() 函数用于在 JS 中对 URL 进行编码,它将 URL 字符串作为参数并返回编码的字符串
本地传值
对比路由传值,传递的路由参数只能在当前页面中使用,若存在共享多个页面或重启浏览器依然能够使用数据,则需要通过本地 storage 方式进行本地读写数据,将打开的控制台切换到 Application,观察左侧 storage 下面:Local Storage、Session Storage、Cookie【IndexDB、Web SQL 暂不考虑】
特性 | cookie | session storage | local storage |
---|---|---|---|
数据生命周期 | 生成时就会被指定一个 maxAge 值,默认关闭浏览器失效 | 页面会话期有效可用 | 除非被清楚,否则一直存在 |
存放数据大小 | 数据不能超过 4kb(每个 http 请求都会携带 cookie) | 没有限制大小 | 没有限制大小 |
与服务器通信 | 参与服务器通信,后端接口可获取 | 不参与服务器通信 | 不参与服务器通信 |
使用是否简易 | 需要自己封装 set、get | 自带 set、get、remove | 自带 set、get、remove |
localStorage.setItem('localdata', 'local data string');
var localdata = localStorage.getItem('localdata');
console.log(localdata); // local data string
localStorage.removeItem('localdata');
sessionStorage.setItem('sessiondata', 'session data string');
var sessiondata = sessionStorage.getItem('sessiondata');
console.log(sessiondata); // session data string
sessionStorage.removeItem('localdata');
localStorage.clear();
sessionStorage.clear();
localStorage.setItem('localdata', JSON.stringify({ name: 'aaa', age: 12 }));
var localdata = localStorage.getItem('localdata');
console.log(localdata); // "{ name: 'aaa', age: 12 }"
console.log(JSON.parse(localdata)); // { name: 'aaa', age: 12 }
标记属性传值
通过 HTML 5 的全局 data-* 属性进行值传递,常用于板块之间的数据传值,前端框架 vue、angule、react 组件传值就是一种标记属性传值的过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button data-num="1" onclick="addNum(this)">+1</button>
<script type="text/javascript">
function addNum(doc) {
var num = doc.getAttribute('data-num');
+num++; // 前面 + 将 num 转换为 Number 类型,后面 ++ 表自增加 1
doc.setAttribute('data-num', num);
doc.innerHTML = '+' + num;
}
</script>
</body>
</html>