函数防抖与函数节流
对于某些 高频触发 的事件,如果事件处理函数的调用频率没有限制的话,那么将会大大加重浏览器的负担
这时我们可以采用防抖函数或节流函数,减少事件处理函数的调用频率,同时保证不会影响用户体验
防抖函数
场景:在触发事件 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/ 百度的搜索地址
 - 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>










