• JS作用域、执行上下文、递归与闭包


    目录

     

    作用域

    全局作用域

    函数作用域

    执行上下文

    函数执行上下文

    执行上下文栈

    作用域与执行上下文的区别

    递归

    闭包

    产生闭包的条件

    闭包的作用

    使用注意 

    内存泄漏

    内存溢出(一种程序运行出现的错误)


    作用域

    作用域指一个变量的作用范围。它是静态的(相对于上下文对象), 在编写代码时就确定了。

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

    全局作用域

    直接编写在script标签中的JS代码,都在全局作用域。

    在全局作用域中:

    • 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用。

    • 创建的变量都会作为window对象的属性保存。

    <script>
        var a = 10;
    </script>
    • 创建的函数都会作为window对象的方法保存。

    <script>
        function say(){
        }
    </script>

    全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问到。

    函数作用域

    调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁。每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。

    在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。简单讲就是里面可以访问外面,但是外面不能访问里面。

    在函数中要访问全局变量可以使用window对象。

    <script>
        var a = 10;
        function say(){
            console.log(window.a);
        }
    </script>

    提醒1:

    在函数作用域也有声明提前的特性:

    • 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明

    • 函数声明也会在函数中所有的代码执行之前执行

    因此,在函数中,没有var声明的变量都会成为全局变量,而且并不会提前声明。

    <script>
        var a = 10;//全局
        function say(){
            var a = 5;//局部变量
            b = 20;//全局
            console.log(window.a);
        }
    </script>

    提醒2:定义形参就相当于在函数作用域中声明了变量。

    <script>
        var a = 10;//全局
        function say(b){
            //b为局部
        }
    </script>

    执行上下文

    函数执行上下文

    在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)。

    (1)对局部数据进行预处理:

    • 形参变量==>赋值(实参)==>添加为执行上下文的属性

    • arguments==>赋值(实参列表), 添加为执行上下文的属性

    • var定义的局部变量==>undefined, 添加为执行上下文的属性

    • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法

    • this==>赋值(调用函数的对象)

    (2)开始执行函数体代码

    执行上下文栈

    • 1.在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象

    • 2.在全局执行上下文(window)确定后, 将其添加到栈中(压栈)

    • 3.在函数执行上下文创建后, 将其添加到栈中(压栈)

    • 4.在当前函数执行完后,将栈顶的对象移除(出栈)

    • 5.当所有的代码执行完后, 栈中只剩下window

    作用域与执行上下文的区别

    区别1:

    • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时

    • 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建

    • 函数执行上下文是在调用函数时, 函数体代码执行之前创建

    区别2:

    • 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化

    • 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放

    联系:

    • 执行上下文(对象)是从属于所在的作用域

    • 全局上下文环境==>全局作用域

    • 函数上下文环境==>对应的函数使用域

    递归

    一个函数通过名字调用自身的情况,好处是代码简洁。在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。

    注意:

    • 找规律,很容易找到规律的使用递归特别方便

    • 出口,必须得有个已知的,没有出口就会一直循环

    缺点:

    • 函数调用导致的时间和空间消耗大

    • 存在重复计算,效率低

    • 调用栈可能会溢出

    <script>
    //递归实现阶乘
        function sayhi(num) {
            if (num <= 1){
                return 1;
            }else {
                return num * sayhi(num-1);
                }
            }
            console.log(sayhi(5));
    </script>
    <script>
    	//递归:一个函数可不可以自己调用自己
    	 function f1() {
    	 	console.log("hello");
    	 	f1();
    
    	 }
    	 
    	//递归就是老和尚讲故事,死循环了,必须要给一个结束的条件才有意义
    	 var i = 0;
    	 function f1() {
    		
    	 	console.log("hello");
    	 	i++;
    	 	if (i < 10) {
    	 		f1();
    	 	}
    		
    
    	// }
    	// f1();
    	// 
    	//求n个数的累加,我们可以采取递归来替代循环
    	 function getSum(n) {
    	 	if (n === 1) {
    	 		return 1;
    	 	}
    
    	 	return n + getSum(n-1);
    
    	 }
    	 console.log (getSum(100));
    	 	
    	//递归调用比较占用资源,循环次数太多不适合使用
    	//
    	//输入一个数,求这个数的各位数字之和
    	//先取余再相除在取整
    	function getSum(n) {
    		//结束条件
    		if (n < 10) {
    			return n;
    		}
    		
    		return n % 10 + getSum(parseInt(n/10));
    	}
    	console.log(getSum(140));
    		 
    </script>

    闭包

    有权访问另外一个函数作用域中的变量的函数。当内部函数被保存在外部时,将会生成闭包。

    产生闭包的条件

    • 1.函数嵌套

    • 2.内部函数引用了外部函数的数据(变量/函数)。

    缺点:闭包导致原有作用链不释放,导致内存泄露(漏得越多,等价占用的内存越多,剩下的可用内存就少了)。

    全局变量和局部变量,该特殊之处在于函数内部可以读取全局变量,但是函数外部是不能读取局部变量的。函数内部声明变量的时候,一定要使用var命令。这就导致一个问题,如果我们需要从外部读取函数内的局部变量的时候,就必须采用一种特殊的方式,在函数内部再定义一个函数。

    <script>
     function f1(){
        var n=22;
        function f2(){
          alert(n); // 22
        }
    
      }
    </script>

    我们只需要将f2作为返回值,就可以读取f1的内部变量了。

    上述f2就是闭包,也就是说闭包是一个可以读取其他函数内部变量的函数;即一个函数内部的函数。

    闭包的作用

    • 读取函数内部的变量,实现公有变量;
    • 可以做缓存,使得变量的值始终保持在内存中;
    • 可以实现封装,属性私有化;
    • 模块化开发,防止污染全局变量

    举例1:

        <script>
            function f1() {
                var a = 1;
                function f2() {
                    a ++;
                    console.log(a);
                }
                return f2;
    
            }
            var f = f1();
            f();//2
            f();//3
            f();//4
            f();//5
        </script>

    ps:外部函数f1执行完毕后,变量a并没有消失,而是保存在了内存中。

    内部采用函数表达式,没有产生闭包的原因如下:

        <script>
            function f1() {
                var re = [];
                for (var i=0; i < 10; i++){
                    re[i] = function () {
                        console.log(i);
                    };
                }
                return re;
            }
            var f = f1();
            for (var j=0; j < 10; j++){
                f[j]();
            }
            // 输出为10个10
            
        </script>

    ps:f1执行完了之后外部i值已经是10了,函数内部只是引用 ,只有外部调用的时候才会运行函数,那时候i就已经是10了,而且10个函数都指向同一个AO。

    举例2:将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数。

    <script>        
        function myModule() {
            //私有数据
            var msg = 'hello'
    
            //操作私有数据的函数
            function doSomething() {
                console.log('doSomething() '); 
            }
    
            function doOtherthing() {
                console.log('doOtherthing() ')) 
            }
    
            //通过【对象字面量】的形式进行包裹,向外暴露多个函数
            return {
                doSomething1: doSomething,
                doOtherthing2: doOtherthing
            }
        }
    </script>

    使用注意 

    • 内存消耗大,理性使用
    • 不要随便改变父函数内部变量的值

    内存泄漏

    内存泄漏:占用的内存没有及时释放。内存泄露积累多了就容易导致内存溢出。

    常见的内存泄露:

    • 1.意外的全局变量

    • 2.没有及时清理的计时器或回调函数

    • 3.闭包

    内存溢出(一种程序运行出现的错误)

    内存溢出:当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。

  • 相关阅读:
    OSPF
    【今日CS 视觉论文速览】 24 Jan 2019
    【今日CS 视觉论文速览】Wed, 23 Jan 2019
    【今日CS 视觉论文速览】 21 Jan 2019
    【Processing学习笔记】安装与入门
    【今日CS 视觉论文速览】Part2, 18 Jan 2019
    【今日CS 视觉论文速览】Fri, 18 Jan 2019
    【今日CS 视觉论文速览】Thu, 17 Jan 2019
    【今日CS 视觉论文速览】Part2, 16 Jan 2019
    【今日CS 视觉论文速览】Wed, 16 Jan 2019
  • 原文地址:https://www.cnblogs.com/markniefeng/p/10561859.html
Copyright © 2020-2023  润新知