前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript 核心特性之《闭包》

JavaScript 核心特性之《闭包》

作者头像
离殊
发布2022-04-01 16:02:39
2590
发布2022-04-01 16:02:39
举报
文章被收录于专栏:DingLin 随笔

什么是闭包?

其实闭包这个话题一直也是面试高频题,我在面试当中有 80% 的时候面试官会问我闭包的特性以及实际的应用场景。闭包也确实是 JavaScript 中的核心特性,在实际当中可以说你一直在使用闭包,只不过你并不知道这个是闭包。

举个例子,一个电子科技爱好者会经常做一些电子零件,但是他并没有上过学,也没有系统的学过这个专业。但是当他准备去大学系统的学习电子专业的时候,其实老师讲过的课他不需要听都可以听得懂,只不过是这些名词儿是他在实验中获得的信息,并不知道叫什么而已。随后只不过是一直的顿悟,知道了这些名字。这也是人们的智慧,给相应的事物提取出一个名字来命名,当大家聊起这个名字的时候,就知道,噢~原来他说的是这个...但是如果你不知道的话,当人们聊起的时候,你其实听不懂说的是什么,但其实你是知道这个的。

闭包也是一样,往下看看,其实你可能就知道了,也许是你在做项目的时候写过,也或许是你看到过类似的实现形式。

咳咳。。

用官方的话来说,闭包可以让你从内部函数访问外部函数作用域。

就是这么一句话就是闭包的精髓,但其实是听不懂的(至少我在学习 JavaScript 的时候,理解他的字面意思,但是并不知道是什么),我再说说我的理解。

比较官方的话语来说是,一个函数的内部变量被外部访问。

用白话来举个例子说是,小明买了一辆汽车,然后为了保护爱车(就像大家买了个新的 iPhone 贴膜一样),买了车窗的贴膜,而且为了私密性(没开车),贴了那种酷酷的黑色,而且这个膜是那种只能在里面看到外面,外面看不到里面的。这你可以理解成是个闭包。(核心一句话就是,可以从里面访问到外面,但是外面无法访问到里面

好,我们接下来就讨论一下刚才开头说的, 在实际当中可以说你一直在使用闭包,只不过你并不知道这个是闭包。 这句话的含义。

大家都用过 function 来声明函数吧,其实呢,你的每一个 function 都是一个闭包,为什么呢?

我们知道,我们写在 JS 环境中直接声明一个变量,这个变量的作用域是在全局作用域下。

我们也知道,当声明了一个 function 时,在 function 中,当前的作用域就在函数作用域下

所以当你声明一个函数时,并在函数当中声明了变量,处理了一些逻辑,其实这个就是闭包,只不过闭包还有一个要求(我们一会在看),你可以理解为这就是闭包。

所以:你的每一个 function 都可以理解为是闭包。

所以:在实际当中可以说你一直在使用闭包,只不过你并不知道这个是闭包。

#闭包的作用

闭包可以干嘛呢?

在 Web 中,你想要这样做的情况特别常见。大部分我们所写的 JavaScript 代码都是基于事件的 — 定义某种行为,然后将其添加到用户触发的事件之上(比如点击或者按键)。我们的代码通常作为回调:为响应事件而执行的函数。

编程语言中,比如 Java,是支持将方法声明为私有的,即它们只能被同一个类中的其它方法所调用。

而 JavaScript 没有这种原生支持,但我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

我们知道在 JS 中是只有全局作用域和函数作用域的(ES6 开始有块级作用域),如果一个变量只为特定的方法或者类来管理是不可以的,只能通过闭包的形式来做私有化变量。

代码语言:javascript
复制
var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

这次我们只创建了一个词法环境,为三个函数所共享:Counter.incrementCounter.decrementCounter.value

该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。

这三个公共函数是共享同一个环境的闭包。多亏 JavaScript 的词法作用域,它们都可以访问 privateCounter 变量和 changeBy 函数。

#闭包例子

大家应该知道闭包的含义了吧,如果还不知道,那么你在看一遍

还记得开头说过闭包还有一个要求吗?现在来一起看一下这个要求,定义是 定义的内部函数引用了父级(或更上级)作用域的变量

先来个 MDN 的例子

代码语言:javascript
复制
function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();

myFunc 是个高阶函数(Higher-Order Function),先定义了 myFuncmakeFunc()makeFunc() 方法返回了 displayName 函数,这个函数里引用了他的 父级 作用域变量 name 。此时如果调用了 myFunc() 的话,相当于会执行了 displayName(),然后呢,displayName() 里执行了 alert 来访问父级作用域链的变量 name ,最终呢形成了一个闭包。

#闭包存在的问题

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

大家可能都听说过,闭包可能造成内存泄漏,导致程序卡死,崩溃。其实不然,如果你了解闭包,会使用闭包,注意一下闭包的特性,是不会出现这种问题的,这种问题一般都是 JavaScript 新手犯的错误,例如在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。原因是这将导致每次构造器被调用时,方法都会被重新赋值一次(也就是说,对于每个对象的创建,方法都会被重新赋值)。

代码语言:javascript
复制
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

在上面的代码中,我们并没有利用到闭包的好处,因此可以避免使用闭包。修改成如下:

代码语言:javascript
复制
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

但我们不建议重新定义原型。可改成如下例子:

代码语言:javascript
复制
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是闭包?
  • #闭包的作用
  • #闭包例子
  • #闭包存在的问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档