首页 前端知识 JS进阶-作用域

JS进阶-作用域

2025-02-24 13:02:34 前端知识 前端哥 576 28 我要收藏

作用域

局部作用域

定义:局部作用域的意思就是,变量只能在它的代码块或者函数内部访问,而不能在外部访问,局部作用域的变量在函数或代码块执行完后会销毁,不会影响全局作用域变量。

function fn(){
    let a = 1;
    console.log(a); // 10
}
test();
console.log(a); // 报错

局部作用域的特性

  • 避免变量污染:局部作用域变量不会影响外部代码,防止变量污染全局作用域。
  • 生命周期:局部变量在函数或代码块执行完后会被销毁,释放内存。
  • 作用域链:局部作用域可以访问外部作用域的变量,但外部作用域无法访问局部变量。

全局作用域

定义:全局作用域指的是变量或函数在整个程序的任何地方都可以访问,且不会被局部作用域所限制。

全局作用域的特点

  1. 声明在任何函数或代码块外部的变量,默认具有全局作用域。
  2. 全局变量在整个程序执行期间都存在,不会被自动销毁(除非手动删除或页面刷新)。
  3. 全局作用域的变量会成为 window(浏览器环境)或 global(Node.js 环境)对象的属性。
  4. 在任何地方(函数、代码块等)都可以访问全局变量。

作用域链

定义:作用域链是js中的变量查找机制!

是一个查找机制!!指的是当一个变量在当前作用域找不到时,js会沿着作用域的层级结构向上查找,直到找到该变量或到达全局作用域。

作用域链的工作原理

  1. 当访问一个变量时,JavaScript 先在当前作用域查找
  2. 如果找不到,就沿着“作用域链”向上查找父级作用域
  3. 一直查找直到全局作用域,如果全局作用域也没有定义这个变量,就会报错 ReferenceError: variable is not defined
let name = "全局的名字";

function sayName() {
    let name = "局部的名字"; // 局部变量遮蔽了全局变量
    console.log(name); // "局部的名字"
}

sayName();
console.log(name); // "全局的名字"

JS垃圾回收机制

定义:也称作GC(Garbage Collection),负责清除不再使用的对象、变量等,以释放内存。

有两种垃圾回收机制:

  • 标记清除(现代浏览器常用):标记所有的变量,代码运行时,移除仍在使用的变量的标记(从根节点开始,可以到达的变量),然后那些被遗忘的变量,就存有标记,垃圾回收机制会清除这些变量,释放内存。

  • 引用计数(早期):每个对象有一个引用计数,某个变量引用该对象的时候标记+1,某个变量不引用对象-1,当某个对象标记为0的时候,回收对象。(存在问题,就是循环引用问题,例:当一个函数内对象互相引用导致)

function createCycle() {
    let obj1 = {};
    let obj2 = {};
    obj1.ref = obj2;
    obj2.ref = obj1; // obj1 和 obj2 互相引用
}
createCycle();
// obj1 和 obj2 作用域结束,但相互引用,无法回收(内存泄漏)

垃圾回收的触发时机

JavaScript 垃圾回收不是实时运行的,浏览器会在合适的时机触发 ,比如:

  1. 内存不足时(浏览器检测到占用内存过高)。
  2. 定期执行(浏览器内部有 GC 触发策略)。
  3. 函数执行结束(局部变量离开作用域)。
  4. 浏览器空闲时(优化性能,减少卡顿)。

闭包

定义:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域

闭包 = 内层函数+外层函数的变量

常见的闭包形式,外部变量可以访问函数内部的变量

function outer(){
    let a = 10;
    function fn(){
        console.log(a)
    }
    fn();
}
// outer(); === fn === function fn(){}
const fun = outer()   // 等价于 const fun = function fn(){}
fun()  //调用函数  能输出  10  ,表明实现了外部函数,使用了内部的值

闭包应用场景1 :内部变量不希望被外部访问改动的情况

如场景:有一个函数,想实现记录用户访问方法的次数,就是每次访问这个函数,变量就+1

let i = 0;
function count (){
    i++;
    console.log('访问了第',i,'次')
}
count();

这样子可以实现,但是,变量i可以被外部访问,也就是说会容易导致变量被改动,导致功能的不准确问题。

使用闭包后

function count(){
    let i = 0;  //声明变量
    function fn (){
        i++;
        console.log('访问了第',i,'次')
    }
    return fn;
}

const fun = count();
fun();

这样子就能实现i变量不被外部访问到,但是又可以实现计数功能。

闭包会造成内存泄漏,因为按照标记清除法来进行垃圾回收的时候,fun永远属于全局的,只有页面刷新才会被回收,然后fun又可以找到fn,fn里面又使用到了i,就会导致i变量不会被回收。

扩展-防抖、节流代码运用闭包思想

复习到这里,我又想起了防抖、节流,也是使用了这一思想,在这里

防抖(在事件触发后,等待一定时间再执行函数,如果在这个时间内又触发了事件,就重新计时。)

function debounce(func, delay) {
    let timeout;
    return function(...args) {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(context, args), delay);
    };
}

// 使用示例
const handleResize = debounce(() => {
    console.log('窗口调整大小');
}, 300);

window.addEventListener('resize', handleResize);

比如这里的变量timeout,就不会被回收掉

节流(规定一个时间间隔,在这个时间间隔内只允许执行一次函数。即使事件被多次触发,也只会在指定的时间间隔内执行一次。)

function throttle(func, limit) {
    let lastFunc;
    let lastRan;
    return function(...args) {
        const context = this;
        if (!lastRan) {
            func.apply(context, args);
            lastRan = Date.now();
        } else {
            clearTimeout(lastFunc);
            lastFunc = setTimeout(() => {
                if ((Date.now() - lastRan) >= limit) {
                    func.apply(context, args);
                    lastRan = Date.now();
                }
            }, limit - (Date.now() - lastRan));
        }
    };
}

// 使用示例
const handleScroll = throttle(() => {
    console.log('滚动事件触发');
}, 1000);

window.addEventListener('scroll', handleScroll);

变量提升&&函数提升

变量提升的话就是var声明的变量,会被前置,但是未赋值

简单理解了一下

console.log(myVar); // 输出: undefined

var myVar = 5;

console.log(myVar); // 输出: 5

function example() {
    console.log(innerVar); // 输出: undefined
    var innerVar = 10;
    console.log(innerVar); // 输出: 10
}

example();

函数提升

函数声明在文件地方,都会被提升到全局,也就是说可以在头部先使用,然后后面再定义

fn()
function fn(){
    console.log('函数提升')
}

但是下面这种声明方法不能提前,下面这种是赋值声明。

fun()
var fun = function(){
    console.log('函数表达式')
}
转载请注明出处或者链接地址:https://www.qianduange.cn//article/21082.html
标签
评论
发布的文章

C/C | 每日一练 (2)

2025-02-24 13:02:49

Linux性能监控工具汇总

2025-02-24 13:02:48

Python常见面试题的详解16

2025-02-24 13:02:48

QQ登录测试用例报告

2025-02-24 13:02:47

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!