JS闭包(Closures)了解一下

简介: 闭包对于前端开发者来说,既十分重要,又非常难理解。如果能很好的理解它,那你将能写出很多高逼格的代码,并且成为人生赢家,赢取白富美。这篇文章,我们来一起简单的认识一下,你走向人生巅峰的敲门砖(闭包)。Note: 闭包不是JS所特有的。它是一个计算机概念,应用的场景也很多。


该文章是直接翻译国外一篇文章,关于闭包(Closures)。

都是基于原文处理的,其他的都是直接进行翻译可能有些生硬,所以为了行文方便,就做了一些简单的本地化处理。

同时也新增了自己的理解,如有不对,请在评论区指出

如果想直接根据原文学习,可以忽略此文。

如果你觉得可以,请多点赞,鼓励我写出更精彩的文章🙏。

前言

闭包对于前端开发者来说,既十分重要,又非常难理解。如果能很好的理解它,那你将能写出很多高逼格的代码,并且成为人生赢家,赢取白富美。

这篇文章,我们来一起简单的认识一下,你走向人生巅峰的敲门砖(闭包)。

Note: 闭包不是JS所特有的。它是一个计算机概念,应用的场景也很多。

函数也是变量

首先,我们需要明确一个JS针对函数的定义或者使用方式::first-class function

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。

这里引用了阮一峰老师对函数的定义

在JS中我们能对值干点啥呢?

值可以是变量

const name = 'Yazeed';
const age = 25;
const fullPerson = {
  name: name,
  age: age
};
复制代码

值也可以是数组

const items = ['Yazeed', 25, { name: 'Yazeed', age: 25 }];
复制代码

值可以被函数返回

function getPerson() {
  return ['Yazeed', 25, { name: 'Yazeed', age: 25 }];
}
复制代码

通过刚才讲述,通过联想是不是可以想到,一般的值能干的事,其实函数也是可以的。

函数可以是变量

const sayHi = function(name) {
  return `Hi, ${name}!`;
};
复制代码

函数可以是数组

const myFunctions = [
  function sayHi(name) {
    return `Hi, ${name}!`;
  },
  function add(x, y) {
    return x + y;
  }
];
复制代码

函数也可以被其他函数返回

一个函数返回另外一个函数,有一个很大气的名字:高阶函数(higher-order function)(如果是React开发者,是不是感觉到很熟悉,React中有高阶组件)

高阶函数也是实现闭包的基础。

function getGreeter() {
  return function() {
    return 'Hi, 北宸!';
  };
}
复制代码

getGreeter返回了一个函数,为了实现最终的方法调用,需要调用两次。

getGreeter(); // 返回被包裹的函数
getGreeter()(); // Hi, 北宸!
复制代码

我们也可以将返回的函数存入到一个变量中。

const greetBei = getGreeter();
greetBei(); // Hi, 北宸!
greetBei(); // Hi, 北宸!
greetBei(); // Hi, 北宸!
复制代码

初识闭包

我们将北宸这个直接写到被包裹函数的变量剔除,通过给getGreeter利用参数来动态的传入。

// 这样我们就可以通过变量来传入想要显示的字符串!
function getGreeter(name) {
  return function() {
    return `Hi, ${name}!`;
  };
}
复制代码

例如

const greetBei = getGreeter('北宸');
const greetNan = getGreeter('南蓁');
greetBei(); // Hi, 北宸!
greetNan(); // Hi, 南蓁!
复制代码

客官且慢,让我们再看看代码

function getGreeter(name) {
  return function() {
    return `Hi, ${name}!`;
  };
}
复制代码

在不知不觉中使用了闭包

外层函数接收name,但是是在嵌套函数中使用了这个变量。

当一个函数通过return返回了一些变量的时候,该函数的生命周期已经完结了,也就意味着函数中局部变量会被GC等内存清理机制给回收。

但是,凡事都有例外,如果return函数的话,被返回的函数还是继续拥有访问外层函数变量的权限。即便是外层函数已经被执行过了。

闭包的优点

正如刚开始说过,利用闭包我们可以写出很多高逼格的代码,所以我们来看看闭包都有啥优点。

数据私有化

对于一些共用的代码模块来说,数据私有化是非常重要的

如果没有数据私有化,任何使用你定义的函数/库/框架都能够随意修改内部变量,导致不可逆转的bug.

示例讲解(银行不存在数据私有)

我们假设,下面的代码能够控制一个银行账户。而accountBalance将会作为全局变量暴露给外面。

let accountBalance = 0;
const manageBankAccount = function() {
  return {
    deposit: function(amount) {
      accountBalance += amount;
    },
    withdraw: function(amount) {
      // ... safety logic
      accountBalance -= amount;
    }
  };
};
复制代码

既然作为一个银行账户accountBalance会是一个比较私密的数据/属性,但是在外部某些操作人员,可以不计后果的随意修改。

accountBalance = '后生,你的余额,有点少哇!';
复制代码

如果遇到这个处理,是不是会气炸。有木有

JavaC++这些基于class的OOP语言都有private的保留字。通过private定义的类变量,是不能够在外界访问和赋值的。

但是很不幸的是,JS现在还没有支持private(现在TC39关于class中是可以的),但是在该语法没有出现的时候,我们可以利用闭包实现私有属性。

银行存在私有变量

此时我们将accountBalance放到了函数内部。

const manageBankAccount = function(initialBalance) {
  let accountBalance = initialBalance;
  return {
    getBalance: function() {
      return accountBalance;
    },
    deposit: function(amount) {
      accountBalance += amount;
    },
    withdraw: function(amount) {
      if (amount > accountBalance) {
        return 'You cannot draw that much!';
      }
      accountBalance -= amount;
    }
  };
};
复制代码

那接下来,我们对这个账户的操作就会很安全

const accountManager = manageBankAccount(0);
accountManager.deposit(1000);
accountManager.withdraw(500);
accountManager.getBalance();
复制代码

Note:其实在上面的实例中,我们没有直接访问accountBalance,而是通过各种函数来控制它。

即使accountBalance是由manageBankAccount创建的,但是由manageBankAccount返回的三个函数通过闭包都拥有对accountBalance的访问权限。

Currying(柯里化)

如果想对柯里化有更深的了解,可以移步到此处。但是不要忘记回来哈。

当一个函数一次接收很多参数,可以通过柯里化处理为多个单元函数。

const add = function(x, y) {
  return x + y;
};
add(2, 4); // 6
复制代码

我们可以通过闭包对函数进行柯里化处理。

const add = function(x) {
  return function(y) {
    return x + y;
  };
};
复制代码

函数经过柯里化处理之后,返回了一个具有访问x/y变量的函数。

const add10 = add(10);
add10(10); // 20
add10(20); // 30
add10(30); // 40
复制代码

如果你不想将函数所有参数都预载,可以对该函数进行柯里化处理。同样的,实现柯里化也只能通过闭包。

React -Hooks-useEffect

如果你实时关注React的技术动向,在16版本发布了一个技术实现hooks。其中useEffect这个API,就是依赖闭包来实现的。

我们这篇文章只是介绍闭包相关的文章,具体关于React的相关最新技术,现在已经有一个大致的规划,所以这里只是简单的一带而过。

Talk is cheap,show you the code

import React from 'react';
import ReactDOM from 'react-dom';
function App() {
  const username = 'yazeedb';
  React.useEffect(function() {
    fetch(`https://api.github.com/users/${username}`)
      .then(res => res.json())
      .then(user => console.log(user));
  });
  return (
    <div className="App">
      <h1>Open Codesandbox console for {username}'s info</h1>
    </div>
  );
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
复制代码

我们修改username,就会发现,在控制台就会显示最新的数据信息。其中username是定义在匿名函数(function() {//....})之外的,但是可以在匿名函数中进行访问。

Note:这里再继续重申一点,关于React的相关文章,译者会有相应的篇幅进行讲述,这里不做展开说明。

Summary

  1. 函数在JS中作为变量使用(first-class function)
  2. 函数能作为返回值,被其他函数返回
  3. 内部函数拥有访问外部函数的变量的权限,即使外部函数已经被执行过了。
  4. 外部函数的变量也被称为state
  5. 因此,闭包也被称为有状态的函数



相关文章
|
14天前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
1天前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
9 2
|
17天前
|
设计模式 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
在本文中,我们将深入探讨JavaScript中的一个重要概念——闭包。闭包是一种强大的编程工具,它允许函数记住并访问其所在作用域的变量,即使该函数在其作用域之外被调用。通过详细解析闭包的定义、创建方法以及实际应用场景,本文旨在帮助读者不仅理解闭包的理论概念,还能在实际开发中灵活运用这一技巧。
|
18天前
|
缓存 JavaScript 前端开发
深入了解JavaScript的闭包:概念与应用
【10月更文挑战第8天】深入了解JavaScript的闭包:概念与应用
|
1月前
|
自然语言处理 JavaScript 前端开发
Javascript中的闭包encloure
【10月更文挑战第1天】闭包是 JavaScript 中一种重要的概念,指函数能够访问其定义时的作用域内的变量,即使该函数在其词法作用域之外执行。闭包由函数及其词法环境组成。作用域链和词法作用域是闭包的核心原理。闭包常用于数据隐藏和封装,如模块模式;在异步操作中也广泛应用,如定时器和事件处理。然而,闭包也可能导致内存泄漏和变量共享问题,需谨慎使用。
|
14天前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理、应用与代码演示
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理、应用与代码演示
|
15天前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript闭包:原理与应用
【10月更文挑战第11天】深入理解JavaScript闭包:原理与应用
14 0
|
2月前
|
JSON JavaScript 前端开发
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
|
2月前
|
缓存 JavaScript 前端开发
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
该文章详细讲解了JavaScript中的作用域、闭包概念及其应用场景,并简要分析了函数柯里化的使用。
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
|
18天前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:概念与应用
【10月更文挑战第8天】深入理解JavaScript中的闭包:概念与应用