持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
前言:
了解和学习前端的小伙伴肯定会听过JavaScript基础的三座大山,分别是作用域和闭包,原型和原型链,异步和单线程。本文主要是了解作用域、作用域链、闭包及节流和防抖。
作用域
作用域是什么
作用域就是变量与函数的可访问范围,规定变量和函数的可使用范围。规定这样的范围是为了避免变量污染(变量名相同导致代码冲突)
js三种作用域
全局作用域(全局变量) : 在函数外面声明的变量
局部作用域(局部变量) : 在函数里面声明的变量
快级作用域(快级变量) : 在分支或循环大括号中声明的变量
注意:局部变量只能在函数内部使用,函数外部无法使用
什么是作用域链
默认情况下,代码处于全局作用域(0级链),当声明一个函数之后就会开辟一个局部作用域(1级),而函数里面又可以声明一个函数,又会形成新的作用域(2级),以此类推形成的结构称之为作用域链
如下图:
变量的访问原则(就近原则)
当你在一个作用域访问变量的之后,首先会看当前作用域有没有声明。如果有则访问。 没有则往上级作用域查找,有没有声明。有则访问,没有则继续往上。直到作用域最顶端0级,如果还没有找到。则程序报错
闭包
这是MDN对闭包做出的解释:
闭包的定义
一句话概括:闭包是一个可以访问其他函数作用域的函数;
闭包 = 函数 + 上下文的引用,闭包不等于函数。以下代码就构成了闭包:
function fn(){
let a = 1
function fn1() {
console.log(a)
}
fn1()
}
闭包的作用
直接作用:解决变量污染问题,让变量被函数保护起来
例:
let num = 0
setInterval(function () {
console.log(num++)
}, 1000)
以上代码中的 num 是一个使用频率很高的变量名,为了避免和其他位置的代码冲突,可以再使用一个函数把以上代码包裹起来,起到保护作用
如下:
function fn() {
let num = 0
setInterval(function () {
console.log(num++)
}, 1000)
}
这样写的话,定时器的匿名函数与num构成了闭包
再改下代码:
function fn() {
let num = 0
function add() {
console.log(num++)
}
setInterval(add, 1000)
}
这样写,具名函数add 与 num 构成了闭包
结论
一个函数内使用了外部的变量,那这个函数和被使用的外部变量一起被称为闭包结构,在实际开发中,通常会再使用一个函数包裹住闭包结构,以起到对变量保护的作用。
闭包的应用
老生常谈的函数节流和防抖
节流
1.节流 : 在一个时间间隔内,多次触发,事件处理函数仅执行1次。
2.节流场景: 解决高频事件(滚动条、鼠标移动) 实际开发: 滚动到最底部加载更多数据
我们以鼠标移动事件为例:
写个类名为box的盒子
// 1. 先将默认的事件处理函数单独封装
function move(){
console.log(1);
}
// 2. 定义一个闭包来控制默认的事件处理函数
function throttle(handler,millsceconds){
// 定义一个初始时间
let t = 0
return function(){
// 事件触时的事件
let n = Date.now()
if(n - t > millsceconds){
handler()
// 将本次事件处理函数执行的时间赋值给t
t = n;
}
}
}
let mm = throttle(move, 200)
document.querySelector('.box').addEventListener('mousemove', mm)
防抖
1.防抖 : 单位时间内,频繁触发事件,只会触发'最后一次'
2.防抖经典场景 : 输入框输入事件 oninput
3.作用:减少事件触发频率,提高网页性能
我们以input事件为例:
写个id名为keywords的input文本框
// 1. 将真正的事件处理函数单独封装
function down(){
console.log('hello');
}
// 2. 在闭包的内部函数里,用setTimeout为真正的事件处理函数设置一个等待时间
function debounced(handler,millseconds){
// 定义变量于存储定时器
let timer;
return function(){
// 清除定时器的操作必须放在创建之前
clearTimeout(timer)
// 创建本次的定时器并保存到timer
timer = setTimeout(handler, millseconds)
}
}
let c = debounced(down, 200)
document.querySelector('#keywords').addEventListener('keyup', c)