• javascript高级(3)


    目录

    执行上下文栈

     测试

     面试题

    作用域

     作用域与执行上下文

    作用域链

     面试题

    闭包

    引入

    常见的闭包

    闭包的作用

    闭包的生命周期

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

    闭包的缺点及解决

    内存溢出

    内存泄漏


    执行上下文栈

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

     测试

    1. console.log('gb'+i);
    2. var i=1
    3. foo(1)
    4. function foo(i){
    5. if(i==4){
    6. return
    7. }
    8. console.log('fb'+i);
    9. foo(i+1)//递归调用:在函数内部调用自己
    10. console.log('fe'+1);
    11. }
    12. console.log('ge'+i);

    输出结果:

     面试题

    1.

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

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

    2.

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

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

    3.

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

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

    1. var c
    2. function c(c){
    3. console.log(c);
    4. }
    5. c=1
    6. c(2)

    作用域

    1.理解

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

    2.分类

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

    3.作用

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

     作用域与执行上下文

    区别1:

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

    区别2:

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

    联系

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

    作用域链

     面试题

    1. var x=10
    2. function fn(){
    3. console.log(x);
    4. }
    5. function show(f){
    6. var x=20
    7. f()
    8. }
    9. show(fn)

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

     2.

    1. var fn=function(){
    2. console.log(fn);
    3. }
    4. fn();

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

    3.

    1. var obj={
    2. fn2:function(){
    3. console.log(this.fn2);
    4. console.log(fn2);
    5. }
    6. }
    7. obj.fn2()

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

    闭包

    引入

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8" />
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    7. <title>Documenttitle>
    8. <script>
    9. window.onload = function () {
    10. var btns = document.getElementsByTagName("button");
    11. // 遍历加监听
    12. for (var i = 0, length = btns.length; i < length; i++) {
    13. var btn = btns[i];
    14. btn.index = i;
    15. btn.onclick = function () {
    16. alert("第" + (this.index+1) + "个");
    17. //alert("第"+(i+1)+"个");
    18. };
    19. }
    20. for(var i=0,length=btns.length;i
    21. (
    22. function (i){
    23. var btn=btns[i]
    24. btn.onclick=function(){
    25. alert('第'+(i+1)+'个')
    26. }
    27. }
    28. )(i)
    29. }
    30. };
    31. script>
    32. head>
    33. <body>
    34. <button>测试1button>
    35. <button>测试2button>
    36. <button>测试3button>
    37. body>
    38. html>

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

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

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

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

    1.如何产生闭包?

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

    2.闭包是什么?

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

    3.产生闭包的条件?

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

    常见的闭包

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

    1. function fn1(){
    2. var a=2
    3. function fn2(){
    4. a++
    5. console.log(a);
    6. }
    7. return fn2
    8. }
    9. var f=fn1()
    10. f()//3
    11. f()//4

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

    1. function showDelay(msg,time){
    2. setTimeout(function(){
    3. alert(msg)
    4. },time)
    5. }
    6. showDelay('atguigu',2000)

    闭包的作用

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

    问题:

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

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

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

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

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

    闭包的生命周期

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

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

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

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

    1. 具有特定功能的js文件
    2. 将所有的数据和功能都封装在一个函数内部(私有的)
    3. 只向外暴露一个包含n个对象或函数
    4. 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Documenttitle>
    8. <script src="92.myModule.js">script>
    9. <script>
    10. var module=myModule();
    11. myModule.doSomething();
    12. script>
    13. head>
    14. <body>
    15. body>
    16. html>
    1. function myModule(){
    2. // 私有数据
    3. var msg='My atguigu'
    4. // 操作数据的函数
    5. function doSomething(){
    6. console.log('doSomething()'+msg.toUpperCase());
    7. }
    8. function doOtherthing(){
    9. console.log('doOtherthing()'+msg.toLowerCase());
    10. }
    11. // 向外暴露对象(给外部使用的方法)
    12. return {
    13. doSomething:doSomething,
    14. doOtherthing:doOtherthing
    15. }
    16. }
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Documenttitle>
    8. <script src="92.myModule2.js">script>
    9. <script>
    10. var myModule2=myModule2()
    11. myModule2.doSomething();
    12. script>
    13. head>
    14. <body>
    15. body>
    16. html>
    1. (
    2. function (){
    3. // 私有数据
    4. var msg='My atguigu'
    5. // 操作数据的函数
    6. function doSomething(){
    7. console.log('doSomething()'+msg.toUpperCase());
    8. }
    9. function doOtherthing(){
    10. console.log('doOtherthing()'+msg.toLowerCase());
    11. }
    12. window.myModule2={
    13. doSomething:doSomething,
    14. doOtherthing:doOtherthing
    15. }
    16. })()

    闭包的缺点及解决

    缺点

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

    解决

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

    内存溢出

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

    内存泄漏

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

    常见的内存泄漏

    • 意外的全局变量
    • 没有及时清理的计时器或回调函数
    • 闭包
    1. // 内存泄漏
    2. function fn(){
    3. a=new Array(100000)
    4. console.log(a);
    5. }
    6. fn()
    7. var intervalId=setInterval(function(){
    8. console.log('--------');
    9. },1000)
    10. // clearInterval(intervalId)
    11. function fn1(){
    12. var a=4
    13. function fn2(){
    14. console.log(++a);
    15. }
    16. return fn2;
    17. }
    18. var fn=fn1()
    19. f()
    20. // f=null

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

  • 相关阅读:
    Mac玩转Linux,如何在虚拟机安装操作系统centos,以及如何进行虚拟机快照、克隆连接,以及网络连接的三种方式,vi/vim编辑器使用
    从苹果、SpaceX等高科技企业的产品发布会看企业产品战略和敏捷开发的关系
    Hive的基本操作
    易基因|ONT:三代原核甲基化在痤疮杆菌噬菌体表观遗传印迹中的工程选择性研究
    云计算概论
    【OAuth2】二十、OAuth2扩展协议 PKCE
    openai chatGPT 原理通俗介绍
    数据结构与算法(java)--哈希表
    Spring MVC 中的数据验证技术
    程序员实现财务自由的5个方法
  • 原文地址:https://blog.csdn.net/m0_62520946/article/details/126175287