闭包函数与箭头函数的区别

    更新时间: 2022-10-29 18:17:36
    点击量: 271
    标签: js前端

    简介:在JavaScript中,闭包函数和箭头函数是两种不同的函数形式,了解各自的特性知道如何去使用

    文章均为个人原创, 搬运请附上原文地址感谢, 原文来自MasterYi博客

    前言

    在JavaScript中,闭包函数和箭头函数是两种不同的函数形式,它们有一些区别。下方我会通过几个例子让大家加深印象

    • 闭包函数是指在其作用域外部定义的函数,但它可以访问其所在作用域内的变量。它创建了一个封闭的作用域,可以保留对其外部变量的引用。闭包函数使用 function 关键字进行定义,并且可以使用 return 语句返回值。
    • 箭头函数是ES6中引入的一种新的函数语法。与闭包函数不同,箭头函数没有自己的作用域,它会继承外部作用域。箭头函数使用箭头( => )语法进行定义,并且可以省略 function 关键字和 return 语句(在某些情况下)。

    以下是闭包函数和箭头函数的一些主要区别:

    1.编译顺序:

    闭包函数会在js预编译过程就被写入内存, 感兴趣可以看另一篇文章【如何理解JS变量提升和上下文问题】,箭头函数不会,因为箭头函数也属于变量函数,只有执行到的时候才会编译写入

        console.log(sumA(1, 2)); // 3
        console.log(sumB(1, 2)); // Cannot access 'sumB' before initialization 
    
        function sumA(a, b){
            return a + b;
        }
    
        const sumB = (a, b) => {
            return a + b;
        }

    2.返回值:

    箭头函数使用箭头( => )语法进行定义,并且可以省略 function 关键字和 return 语句(在某些情况下)

    const sum = (a, b) => a + b;
    console.log(sum(1, 2)); // 3

    3.arguments对象:

    闭包函数有自己的 arguments 对象,可以访问传入的参数。

    function sumA(){
        console.log(arguments); //Arguments(2) [1, 2];
        return arguments[0] + arguments[1];
    }
    console.log(sumA(1, 2))
    
    const sumB = () => {
        console.log(arguments); // arguments is not defined
        return arguments[0] + arguments[1];
    }
    console.log(sumB(1, 2))
    
    // 如果箭头函数想像闭包函数那样传值不固定长度采用这种结构的写法即可
    const sumC = (...arg) => {
        console.log(arg);
        return arg[0] + arg[1];
    }
    console.log(sumC(1, 2))

    4.原型:

    闭包函数有自己的原型,可以使用作构造函数。而箭头函数没有prototype属性,不能用作构造函数。

    function Animal(name, sex, age){
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    const lion = new Animal('狮子', '公', 12);
    console.log(lion);// Animal{ name: '狮子', sex: '公', age: 5 }

    5.this指向:

    箭头函数没有自己的this值,会继承外部作用域的this值。闭包函数中的 this 指向调用它的对象或者全局对象,可以通过call, apply, bind改变。

    const animalA = {
        name: '猫科',
        sleep: function (){
            console.log(this.name)
        }
    }
    animalA.sleep()
    
    const animalB = {
        name: '猫科',
        sleep: () => {
            console.log(this.name)
        }
    }
    animalB.sleep() // Cannot read properties of undefined (reading 'name')
     function Animal(name, sex, age){
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.status = '饿了';
    
        // 睡觉
        this.sleep =  function(hour){
            this.status = '饿了';
            console.log(this, '睡了'+hour)
        }
        // 捕猎
        this.hunt = function (ani, n){
            this.status = '饱了';
            console.log(this, '捕猎了' + n +'只'+ ani)
        }
    }
    
    // 还是上一个例子,随便创建一种基本动物,待会改变this指向
    const basic = new Animal('猫科动物', '未知', 0);
    
    console.log(basic);// Animal{ name: '猫科动物', sex: '未知', age: 0, status: '饿了', sleep, hunt... }
    
    const lion = { name: '狮子', sex: '公', age: 5};
    basic.sleep.call(lion, '12小时');//{name: '狮子', sex: '公', age: 5, status: '饿了'} '睡了12小时'
    basic.hunt.call(lion, '斑马', 2);//{name: '狮子', sex: '公', age: 5, status: '饱了'} '捕猎了2只斑马'
    
    const tiger = { name: '老虎', sex: '母', age: 7};
    // apply和call的区别就是传值,call是(arg1, arge2, arge3), apply是数组[arg1, arge2, arge3]
    basic.hunt.apply(tiger, ['鹿', 1]);// {name: '老虎', sex: '母', age: 7, status: '饱了'} '捕猎了1只鹿'

    6.私有变量:

    闭包函数可以创建私有变量和函数,可以用于创建具有持久状态的函数,缺点是内存不会被自动回收, 需主动设置为null才会回收;

    function workingYears() {
        let year = 0;
        return () => ++year;
    }
    let working = workingYears();
    console.log(working()); // 1
    console.log(working()); // 2

    总结

    过多的闭包会导致代码难以理解和维护,同时也会增加内存开销。及时释放对闭包的引用,避免闭包中的循环引用导致内存泄漏, 可以使用一个对象来缓存已经创建的闭包避免重复创建闭包。