javascript高级(3)

发布于:2022-08-06 ⋅ 阅读:(324) ⋅ 点赞:(0)

目录

执行上下文栈

 测试

 面试题

作用域

 作用域与执行上下文

作用域链

 面试题

闭包

引入

常见的闭包

闭包的作用

闭包的生命周期

闭包的应用——自定义JS模块

闭包的缺点及解决

内存溢出

内存泄漏


执行上下文栈

  1. 在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  3. 在函数执行上下文创建后,将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移除(出栈)
  5. 当所有的代码执行完后,栈中只剩下window
var a=10
        var bar=function(x){
            var b=5
            foo(x+b)
        }
        var foo=function(y){
            var c=5
            console.log(a+c+y);
        }
        bar(10)

 测试

console.log('gb'+i);
        var i=1
        foo(1)
        function foo(i){
            if(i==4){
                return
            }
            console.log('fb'+i);
            foo(i+1)//递归调用:在函数内部调用自己
            console.log('fe'+1);
        }
        console.log('ge'+i);

输出结果:

 面试题

1.

function a(){}
        var a;
        console.log(typeof a);

注意:先执行变量提升,后执行函数提升。因此,typeof a为‘function’,而不是undefined

2.

if(!(b in window)){
            var b=1
        }
        console.log(b);

在变量中,var会变量提升。所以,b定义了但没有赋值。在if语句中,if语句不管前置条件达不达到,后置语句中的变量会声明提升,所以结果为undefined

3.

 var c=1;
        function c(c){
            console.log(c);
            var c=3
        }
        c(2)//报错

注意:在提升之前,函数已经执行完了。因此,可以看成以下代码

var c
        function c(c){
            console.log(c);
        }
        c=1
        c(2)

作用域

1.理解

  • 就是一块“地盘”,一个代码所在的区域
  • 它是静态的(相对于上下文对象),在编写代码时就确定了

2.分类

  • 全局作用域
  • 函数作用域
  • 没有块作用域(ES6有了)

3.作用

  • 隔离变量,不同作用域下同名变量不会有冲突

 作用域与执行上下文

区别1:

  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
  • 全局执行上下文环境是在全局作用域确定之后,JS代码马上执行创建
  • 函数执行上下文是在调用函数时,函数体代码执行之前创建

区别2:

  • 作用域是静态的,只要函数定义好了就一直处在,且不会再变化
  • 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放

联系

  • 执行上下文(对象)是从属于所在的作用域
  • 全局上下文环境==>全局作用域
  • 函数上下文环境==>对应的函数作用域

作用域链

 面试题

var x=10
        function fn(){
            console.log(x);
        }
        function show(f){
            var x=20
            f()
        }
        show(fn)

由作用域划分可得以下,fn()会向上找x,因此为x=10

 2.

var fn=function(){
            console.log(fn);
        }
fn();

在全局作用域里面找变量fn,所以输出为function(){console.log(fn); }

3.

var obj={
            fn2:function(){
                console.log(this.fn2);
                 console.log(fn2);
            }
        }
        obj.fn2()

调用obj中的fn2属性,属性的内容为function,输出this.fn2是在obj中找到fn2.function(){console.log(this.fn2);console.log(fn2);}        注意fn2是属性,不是变量,在查找fn2时,window里面没有fn2这个属性,就会报错

闭包

引入

<!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>
    <script>
      window.onload = function () {
        var btns = document.getElementsByTagName("button");
        // 遍历加监听
        for (var i = 0, length = btns.length; i < length; i++) {
          var btn = btns[i];
          btn.index = i;
          btn.onclick = function () {
            alert("第" + (this.index+1) + "个");
        //alert("第"+(i+1)+"个");
          };
        }
        for(var i=0,length=btns.length;i<length;i++){
            (
                function (i){
                    var btn=btns[i]
                    btn.onclick=function(){
                        alert('第'+(i+1)+'个')
                    }
                }
            )(i)
        }
      };
    </script>
  </head>
  <body>
    <button>测试1</button>
    <button>测试2</button>
    <button>测试3</button>
  </body>
</html>

问:alert("第"+(i+1)+"个");为什么执行出来是i=3?

答:因为事件的响应函数是点击的时候才触发调用的,在点击之前只是声明了但并没有调用过响应函数

问:for (var i = 0, length = btns.length; i < length; i++)为什么不写var =0;i<btns.length;i++?

答:当循环执行一次,btns.length就需要重新计算,因此需要计算多次,这样会提高系统的复杂度。当令length=btns.length时,就只要计算一次。

1.如何产生闭包?

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

2.闭包是什么?

  • (使用chrome调试查看)
  • 理解1:闭包是嵌套的内部函数(绝大部分人)
  • 理解2:包含被引用变量(函数)的对象(极少数人)
  • 注意:闭包存在于桥套的内部函数中

3.产生闭包的条件?

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)

常见的闭包

1.将函数作为另一个函数的返回值

function fn1(){
            var a=2
        function fn2(){
            a++
            console.log(a);
        }
        return fn2
        }
        var f=fn1()
        f()//3
        f()//4

2.将函数作为实参传递给另一个函数调用

function showDelay(msg,time){
            setTimeout(function(){
                alert(msg)
            },time)
        }
        showDelay('atguigu',2000)

闭包的作用

  1. 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部都可以操作(读写)到函数内部的数据(变量/函数)

问题:

1.函数执行完,函数内部声明的局部变量是否还存在?

  • 一般是不存在的。存在于闭包中的变量才可能存在。fn1返回的是fn3的数据,fn3内部数据是地址值指向它在堆空间的对象,fn1将fn3的地址值赋值给了f,所以函数就算释放出栈了,f也能自己去访问到fn3的对象

2.在函数外部能直接访问函数内部的局部变量吗?

  • 不能,但我们可以通过闭包让外部操作它

总结:闭包也是会被回收的,需要定义一个变量来一直指向闭包函数体的地址值,让闭包一直存在

闭包的生命周期

产生:在嵌套内部函数定义执行完时就产生了(不是在调用)

死亡:在嵌套的内部函数称为垃圾对象时

function fn1(){
            // 此时闭包就已经产生了(函数提升,内部函数对象已经创建了)
            var a=2
        function fn2(){
            a++
            console.log(a);
        }
        return fn2
        }
        var f=fn1()
        f()//3
        f()//4
        f=null//闭包死亡(包含闭包的函数对象成为垃圾对象)

闭包的应用——自定义JS模块

  1. 具有特定功能的js文件
  2. 将所有的数据和功能都封装在一个函数内部(私有的)
  3. 只向外暴露一个包含n个对象或函数
  4. 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
<!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>
    <script src="92.myModule.js"></script>
    <script>
        var module=myModule();
        myModule.doSomething();
    </script>
</head>
<body>
    
</body>
</html>
function myModule(){
    // 私有数据
    var msg='My atguigu'
    // 操作数据的函数
    function doSomething(){
        console.log('doSomething()'+msg.toUpperCase());
    }
    function doOtherthing(){
        console.log('doOtherthing()'+msg.toLowerCase());
    }
    // 向外暴露对象(给外部使用的方法)
    return {
        doSomething:doSomething,
        doOtherthing:doOtherthing
    }
}
<!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>
    <script src="92.myModule2.js"></script>
    <script>
        var myModule2=myModule2()
        myModule2.doSomething();
    </script>
</head>
<body>
    
</body>
</html>
(
    function (){
        // 私有数据
        var msg='My atguigu'
        // 操作数据的函数
        function doSomething(){
            console.log('doSomething()'+msg.toUpperCase());
        }
        function doOtherthing(){
            console.log('doOtherthing()'+msg.toLowerCase());
        }

        window.myModule2={
            doSomething:doSomething,
        doOtherthing:doOtherthing
        }
    })()

闭包的缺点及解决

缺点

  • 函数执行后,函数内的局部变量没有释放,占用内存事件会变长
  • 容易造成内存泄漏

解决

  • 能不用闭包就不用
  • 及时释放

内存溢出

  • 一种程序运行出现的错误
  • 当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误

内存泄漏

  • 占用的内存没有被及时释放
  • 内存泄漏积累多了就会内存溢出

常见的内存泄漏

  • 意外的全局变量
  • 没有及时清理的计时器或回调函数
  • 闭包
// 内存泄漏
        function fn(){
            a=new Array(100000)
            console.log(a);
        }
        fn()

        var intervalId=setInterval(function(){
            console.log('--------');
        },1000)
        // clearInterval(intervalId)

        function fn1(){
            var a=4
            function fn2(){
                console.log(++a);
            }
            return fn2;
        }
        var fn=fn1()
        f()
        // f=null

注意:this的指向,当以方法调用时this指向方法所在的对象,当以函数调用时,this就是全局

本文含有隐藏内容,请 开通VIP 后查看