• JavaScript的函数



    函数,其实也是一种对象,每一个函数都是Function类型的实例,可以定义不同类型的属性和方法。

    函数类型

    函数分为函数声明、函数表达式和构造函数

    1. 函数声明,是直接使用function关键字接一个函数名,
    // 函数声明式
    function sum(param1, param2) {
       return param1 + param2;
    }
    
    1. 函数表达式,类似于一个普通变量的初始化,
    let sum = function(param1, param2) {
       return param1 + param2;
    }
    
    1. Function构造函数,通过new操作符,调用Function构造函数。
    const  add  = new Function('a','b','return a + b')
    console.log(add(1,2));
    

    Function构造函数用得比较少,因为Function构造函数的每一次执行,都会解析函数主体,创建一个新的函数对象,如果是一个频繁调用的函数,这样效率非常低。

    另一个原因是Function创建的函数一直作为顶级函数来执行,如果我们在一个函数A中调用Function构造函数,其中的函数体不能访问函数A的局部变量,如下代码:

    <!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>
    </head>
    
    <body>
        <script>
            var y = 'global';  // 全局环境定义的y值
            function constructFunction() {
                var y = 'local';  // 局部环境定义的y值
                return new Function('return y');  // 无法获取局部环境定义的值 
            }
            console.log(constructFunction()()); // 输出'global'
        </script>
    </body>
    
    </html>
    

    arguments对象

    arguments对象是所有函数都具有的一个内置局部变量,表示是函数实际接收的参数,arguments是一个类数组的结构。arguments对象只能在函数体内使用,如下代码:

    (function (a, b) {
        console.log(arguments); // [Arguments] { '0': 2 }
    })(2)
    

    arguments是表示实际参数的,所以就由实际参数决定,如上代码中,定义的形参有两个,实参却只有一个,那么arguments对象的值也只有一个。

    构造函数

    构造函数和和普通函数的区别:

    1. 构造函数的函数名的首字母是大写;
    2. 构造函数体的this关键字,表示是实例对象,构造函数一般不会返回任何值,默认是返回this;比如:
    function Do(){
        this.name = 'play'
    }
    const aa = new Do()
    
    console.log(aa.name); // play
    
    1. 构造函数调用的时候,需要与new操作符配合使用

    作用域

    一个变量的定义和调用都是在一个范围内,这个范围就是作用域。
    作用域分为全局作用域、函数作用域和块级作用域

    全局作用域和块级作用域比较简单,这里仅仅总结一下函数作用域。

    在函数内部使用var关键字声明变量的时候,函数中会存在变量提升的问题,如下代码:

    (function(){
        console.log(a); // undefined
        var a =908;
    })()
    

    在变量a声明之前打印,输出结果为undefined。这段代码等同于:

    (function(){
        var a ;
        console.log(a); // undefined
    })()
    

    这就是变量提升,将变量的声明提升到函数的顶部位置,但是变量的赋值并没有提升。
    只有通过var关键字声明的变量才会出现提升的问题

    使用函数声明方式来定义一个函数,也会出现函数提升问题,通过函数表达式声明函数不会出现函数提升问题。

    add(1, 2); // 3
    function add(a, c) {
        console.log(a + c)
    }
    

    闭包

    闭包是一个拥有多个变量和绑定这些变量执行上下文环境的表达式,一般都是一个函数。

    函数拥有外部变量的引用,在函数执行完成后,该变量没有被销毁,处于活跃状态;
    当闭包作为一个函数返回的时候,它的执行上下文环境没有被销毁;

    这样闭包就导致大量消耗内存,闭包使用越多,内存消耗就越大。

    但是闭包也有一些有点:

    1. 结果缓存,比如有一个很耗时的函数对象,在每次调用都消耗很长时间。这时候我们可以将函数的处理结果在内存中缓存,这样在执行代码的时候,如果内存中存有结果,就直接返回,否则再执行一次函数并且更新缓存结果。比如以下代码:
    var cachedBox = (function () {
        // 缓存的容器
        var cache = {};
        return {
            searchBox: function (id) {
                // 如果在内存中,则直接返回
                if (id in cache) {
                    return '查找的结果为:' + cache[id];
                }
                // 经过一段很耗时的dealFn()函数处理
                var result = dealFn(id);
                // 更新缓存的结果
                cache[id] = result;
                // 返回计算的结果
                return '查找的结果为:' + result;
            }
        };
    })();
    // 处理很耗时的函数
    function dealFn(id) {
        console.log('这是一段很耗时的操作');
        return id;
    }
    // 两次调用searchBox()函数
    console.log(cachedBox.searchBox(1));
    console.log(cachedBox.searchBox(1));  
    
    1. 封装,在进行代码模块化封装的时候,需要把一些具有一定特征的属性封装在一起,只需要对外暴露对应的函数就好,不用担心内部逻辑的实现。比如数据结构中的栈:
    let stack=(
        function() {
            let items = [];
            return {
                push:function (element) {
                    items.push(element)
                },
                pop:function () {
                    return items.pop();
                },
                size:function () {
                    return items.length;
                }
            }
        }
    )()
    
    stack.push(90);
    stack.push(100);
    
    console.log(stack.size());// 2
    

    总结闭包的优点:

    1. 保护函数内变量的安全,实现封装,防止变量流入其他环境发生命名冲突
    2. 在适当的时候,可以在内存中维护变量缓存,提升执行效率

    闭包缺点:

    1. 内存消耗,一般情况下函数的活动会随着执行上下文环境一起被销毁,但是闭包引用的是外部的函数活动对象,在闭包函数执行完后,这个活动对象没有销毁,导致闭包比一般的函数更消耗内存;
    2. 内存泄漏,如果闭包中的作用域链中有DOM对象,那么这个DOM对象无法销毁,造成内存泄漏。
  • 相关阅读:
    oracle数据库报列表中最大表达式为1000错误
    【计算机毕业设计】35.流浪动物救助及领养管理系统源码
    【Spring源码解析】一文读懂Spring注入模型:开发者必备知识
    SpringBoot整合XXLJob
    sanic 教程
    ISO14001环境管理体系认证所需材料
    【图像检测】基于计算机视觉实现地质断层结构的自动增强和识别附matlab代码
    基于百度AI人脸识别+uniapp+springboot的高校防疫小程序
    Java中的集合内容总结——Collection接口
    获取学习资源,方法对了轻松百倍
  • 原文地址:https://blog.csdn.net/xuelian3015/article/details/127037779