1. 首页

【进阶6期】深入浅出图解作用域链和闭包

红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数
关键在于下面两点:

  • 是一个函数
  • 能访问另外一个函数作用域中的变量

对于闭包有下面三个特性:

  • 1、闭包可以访问当前函数以外的变量
function getOuter(){
  var date = '815';
  function getDate(str){
    console.log(str + date);  //访问外部的date
  }
  return getDate('今天是:'); //"今天是:815"
}
getOuter();
  • 2、即使外部函数已经返回,闭包仍能访问外部函数定义的变量
function getOuter(){
  var date = '815';
  function getDate(str){
    console.log(str + date);  //访问外部的date
  }
  return getDate;     //外部函数返回
}
var today = getOuter();
today('今天是:');   //"今天是:815"
today('明天不是:');   //"明天不是:815"
  • 3、闭包可以更新外部变量的值
function updateCount(){
  var count = 0;
  function getCount(val){
    count = val;
    console.log(count);
  }
  return getCount;     //外部函数返回
}
var count = updateCount();
count(815); //815
count(816); //816

作用域链

Javascript中有一个执行上下文(execution context)的概念,它定义了变量或函数有权访问的其它数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

详情查看 【进阶1-2期】JavaScript深入之执行上下文栈和变量对象

作用域链:当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。

作用域链和原型继承查找时的区别:如果去查找一个普通对象的属性,但是在当前对象和其原型中都找不到时,会返回undefined;但查找的属性在作用域链中不存在的话就会抛出ReferenceError

作用域链的顶端是全局对象,在全局环境中定义的变量就会绑定到全局对象中。

全局环境

无嵌套的函数
// my_script.js
"use strict";

var foo = 1;
var bar = 2;

function myFunc() {

  var a = 1;
  var b = 2;
  var foo = 3;
  console.log("inside myFunc");

}

console.log("outside");
myFunc();

定义时:当myFunc被定义的时候,myFunc的标识符(identifier)就被加到了全局对象中,这个标识符所引用的是一个函数对象(myFunc function object)。

内部属性[[scope]]指向当前的作用域对象,也就是函数的标识符被创建的时候,我们所能够直接访问的那个作用域对象(即全局对象)。

JS中文网 – 前端进阶资源分享,什么是作用域链要理解作用域链,需要先清楚下面两个概念 www.javascriptc.com

myFunc所引用的函数对象,其本身不仅仅含有函数的代码,并且还含有指向其被创建的时候的作用域对象。

调用时:当myFunc函数被调用的时候,一个新的作用域对象被创建了。新的作用域对象中包含myFunc函数所定义的本地变量,以及其参数(arguments)。这个新的作用域对象的父作用域对象就是在运行myFunc时能直接访问的那个作用域对象(即全局对象)。

JS中文网 – 前端进阶资源分享 www.javascriptc.com,作用域作用域,是指变量的生命周期。全局作用域函数作用域块级作用域词法作用域动态作用域1、全局作用域全局变量:生命周期将存在于整个

有嵌套的函数

当函数返回没有被引用的时候,就会被垃圾回收器回收。但是对于闭包,即使外部函数返回了,函数对象仍会引用它被创建时的作用域对象。

"use strict";
function createCounter(initial) {
  var counter = initial;

  function increment(value) {
    counter += value;
  }

  function get() {
    return counter;
  }

  return {
    increment: increment,
    get: get
  };
}

var myCounter = createCounter(100);
console.log(myCounter.get());   // 返回 100

myCounter.increment(5);
console.log(myCounter.get());   // 返回 105

当调用 createCounter(100) 时,内嵌函数increment和get都有指向createCounter(100) scope的引用。假设createCounter(100)没有任何返回值,那么createCounter(100) scope不再被引用,于是就可以被垃圾回收。

概要. 对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数,JS中文网 – 前端进阶资源分享 www.javascriptc.com

但是createCounter(100)实际上是有返回值的,并且返回值被存储在了myCounter中,所以对象之间的引用关系如下图:

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现,JS中文网 – 前端进阶资源分享 www.javascriptc.com

即使createCounter(100)已经返回,但是其作用域仍在,并且只能被内联函数访问。可以通过调用myCounter.increment() 或 myCounter.get()来直接访问createCounter(100)的作用域。

当myCounter.increment() 或 myCounter.get()被调用时,新的作用域对象会被创建,并且该作用域对象的父作用域对象会是当前可以直接访问的作用域对象。

调用get()时,当执行到return counter时,在get()所在的作用域并没有找到对应的标示符,就会沿着作用域链往上找,直到找到变量counter,然后返回该变量。

概要对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数。 关键点,JS中文网 – 前端进阶资源分享 www.javascriptc.com

单独调用increment(5)时,参数value保存在当前的作用域对象。当函数要访问counter时,没有找到,于是沿着作用域链向上查找,在createCounter(100)的作用域找到了对应的标示符,increment()就会修改counter的值。除此之外,没有其他方式来修改这个变量。闭包的强大也在于此,能够存贮私有数据。

作用域链与闭包· Sample GitBook,JS中文网 – 前端进阶资源分享 www.javascriptc.com

创建两个函数:myCounter1myCounter2

//my_script.js
"use strict";
function createCounter(initial) {
  /* ... see the code from previous example ... */
}

//-- create counter objects
var myCounter1 = createCounter(100);
var myCounter2 = createCounter(200);

关系图如下

javascript深入浅出图解作用域链和闭包,JS中文网 – 前端进阶资源分享 www.javascriptc.com

myCounter1.increment和myCounter2.increment的函数对象拥有着一样的代码以及一样的属性值(name,length等等),但是它们的[[scope]]指向的是不一样的作用域对象。

参考

从作用域链谈闭包

JS中文网 – 前端进阶资源分享 www.javascriptc.com

往期目录:

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文来源于网络,其版权属原作者所有,如有侵权,请与小编联系,谢谢!

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:【进阶6期】深入浅出图解作用域链和闭包

链接:https://www.javascriptc.com/438.html

原文链接:https://github.com/yygmind/blog/issues/17

« 【进阶7期】JavaScript深入之从作用域链理解闭包
【进阶5期】JavaScript深入之4类常见内存泄漏及如何避免»
Flutter 中文教程资源

相关推荐

QR code