一、闭包
谈到闭包,我们首先要讨论的就是作用域。
1、作用域: 是指程序源代码中代码定义的范围。规定了如何设置变量,也就是确定了当前执行代码对变量的访问权限。
JavaScript采用词法作用域,也就是静态作用域,就是在函数定义的时候,就已经确定了。
2、变量对象:
变量对象就是当前代码段中,所有的变量(包括变量、函数、形参argument)组成的一个对象。
变量对象是在执行上下文中被激活的,只有变量对象被激活了,在这段代码中才能使用所有的变量。
变量对象分为全局变量对象、局部变量对象。全局也叫VO,函数由于执行才被激活为AO。
3、作用域链:
4、闭包的使用:
下面代码是闭包的定义:
a函数执行完之后,a函数作用域断裂销毁,b函数被return,b函数还能访问到a函数,是因为b函数在生成的时候时候也保存了一份a函数变量的引用。
小结:闭包产生的原因必须结合作用域链进行讲解。
5、闭包的实战应用:
防抖、节流、定时器传参
// 1、递归的方式实现
// 考察三个知识点:闭包、递归、全局变量
function add(n) {
// 递归的出口
if (!n) return res;
res = n
return function (n) {
return add(res + n);
};
}
add(1)(2)(); // 3
console.log('add(1)(2)(): ',add(1)(2)())
add(); // 函数的调用
二、深浅拷贝深层次理解
1、下面是复制举例,并不是浅拷贝(浅拷贝也是会创建一个新的对象的):
let obj = {
name: '荒天帝'
}
let obj2 = obj
obj2.name = '石昊'
console.log('obj.name',obj.name) // 石昊
console.log('obj2.name',obj2.name)// 石昊
console.log(obj === obj2) // true
console.log( {} == {}) //false
注:浅拷贝拷贝基本数据类型,是将值复制一份;如果是引用类型,那就是拷贝的地址。
2、深拷贝实现
function deepClone(source) {
if (source === null) return source;
if (source instanceof Date) return new Date(source);
if (source instanceof RegExp) return new RegExp(source);
// 递归出口
if (typeof source !== "object") return source;
let obj = Array.isArray(source) ? [] : {};
for (let i in source) {
if (source.hasOwnProperty(i)) {
obj[i] = deepClone(source[i]);
}
}
return obj;
}
let person = {
name: 'John',
hobby: ['zhangsang', 'lisi'],
date: new Date()
}
let deepPerson = deepClone(person);
deepPerson.name = '狠人大帝'
console.log('deepPerson',deepPerson)
console.log('person',person)
三、防抖节流
我是这么理解的:防抖就是回城,打断重来;节流就是技能cd
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" id="input" />
</body>
<script>
// 防抖函数
function debounce(callback, delay) {
let timer;
return function (arg) {
clearTimeout(timer);
// 我们想清除的是setTimeout,我们应该存储这个timer的变量
// timer变量需要一直保存在内存当中, 内存的泄露,这就要使用闭包了
timer = setTimeout(function () {
callback(arg);
}, delay);
};
}
function func(value) {
console.log("value", value);
}
const input = document.getElementById("input");
const debounceFn = debounce(func, 1000);
input.addEventListener("keyup", function (e) {
debounceFn(e.target.value);
});
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="button">点击</button>
<script>
function throttle(func, wait) {
let timerout;
return function () {
if (!timerout) {
timerout = setTimeout(function () {
func();
timerout = null
}, wait);
}
};
}
document.querySelector("#button").onclick = throttle(function () {
console.log("节流 ", Math.random());
}, 1000);
</script>
</body>
</html>
五、登录验证
1、传统sessionId登录验证弊端, 解决:基于token身份认证方案
六、面试真题
function fn(a, c) {
console.log("a", a); // a [Function: a]
var a = 123;
console.log("a", a); // a 123
console.log("c", c); // c [Function: c]
function a() {}
if (false) {
var d = 678;
}
console.log("d", d); // d undefined
console.log("b ", b); // b undefined
var b = function () {};
console.log("b ", b); // b [Function: b]
function c() {}
console.log("c ", c); // c [Function: c]
}
fn(1, 2);
// 分析:
/**
* 1、创建了AO对象
* 2、找形参和变量的声明,作为AO对象的属性名,值是undefined,实参和形参相统一
* 3、找函数声明,会覆盖变量的声明
*/
AO: {
a: undefined 1 function a() {}
c: undefined 2 function c() {}
d: undefined
b: undefined
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 阿里真题
var name = 222;
var a = {
name: 111,
say: function () {
console.log(this.name);
},
};
var fun = a.say;
fun(); // 222
a.say(); // 111
var b = {
name: 333,
say: function (fun) {
fun();
},
};
b.say(a.say); // 222
b.say = a.say;
b.say(); // 333
</script>
</body>
</html>