作用域作用域链及闭闭包

作用域作用域链及闭闭包

1作用域作用域链

作用域:我相信很多学习者都学过一些编程语言,作用域应该并不陌生,用我自己理解来说,作用域就是规定变量的使用的一个范围

作用域链:用我自己的理解来说,作用域链是一种层级关系,就是一个大的作用域范围嵌套了几个小的作用域范围

1.1作用域

在javascript(ES5)中有两种类型的作用域

函数作用域:(函数内部作用域)

  • 在函数内部声明的变量,会成为全局作用域
  • 函数内部声明的变量,函数外部不能声明

全局作用域:(函数外部作用域)

  • 函数之外声明的变量,会成为全局变量。

  • 函数外部声明的变量,在函数内部可以访问。

  • 当函数嵌套,在这个时候,内部函数与外部函数的这个变量就组成了闭包。

以上可以归结为:**函数内部作用域 ** 和 函数外部作用域

在其他语言中也有作用域,其实大部分的规定可以在javascript中使用,但是在javascript也有跟其他语言不同的规定

举个例子:

{
  var a = 10
}
console.log(a); //a

在其他语言中,看到{}包含着的变量,在其他语言中一般是不可调用的,但是在javascript中可以使用

1.2作用域

在了解作用域链之前先了解一下什么是自由变量

var a = 100
function fn() {
    var b = 200
    console.log(a) // 这里的a在这里就是一个自由变量  // 100
    console.log(b)
}
fn()

简单理解就是在 要使用的那个作用域中找不到定义的变量

代码中console.log(a) 这条语句的作用域是在函数 fn中的 此时的 a 不在此层作用域,所以叫做自由变量。

理解了自由变量之后,让我们来理解一下作用域链

var a = 100
function F1() {
  var b = 200
  function F2() {
    var c = 300
    console.log(a) // 自由变量,顺作用域链向父作用域找 //100
    console.log(b) // 自由变量,顺作用域链向父作用域找 //200
    console.log(c) // 本作用域的变量  //300
  }
  F2()
}
F1()

上面的例子中

F2函数作用域中,只用c声明定义了,ab都没有声明定义中,所以为自由变量。

那么自由变量会怎么获取它本身的值呢?

答案是:自由变量会顺着生成 当前函数的作用域 的父作用域查找

在这里插入图片描述

这种向着父作用域,查找的链式结构就可以理解成为作用域。简单来说就是,嵌套,儿子找爸爸

2闭包

闭包就是能够读取其他函数内部变量的函数。在javascript中,可以将闭包理解成“函数中的函数“。

理解了作用域和作用域链之后,我们来说说闭包

假设有一种情况

function f1() {
  var n = 999;
}
console.log(n)

外面向获取函数中的局部变量,是获取不到的,所以我们必须得打造一个闭包。

function f1() {
  var n = 999;
  function f2(){
     console.log(n)
  }
  return f2
}
var fn = f1()
fn()  //999

上面代码中 f2函数就是一个闭包,能够获取局部变量。

那么闭包有什么作用呢?

  1. 可以读取外层函数内部的变量
  2. 让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。

第一点在上面已经举例,下面我们来理解第二个作用

例如

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

return 返回的匿名函数是一个闭包

这个时候他会让外部调用的变量,一直储存到它里面闭包环境中。所以每次调用的时候不是5,而是依次递增。

3作用域作用域链及闭闭包习题

如果你看了上面的内容还是不太理解的话,没关系,我们做几道题让你加深理解

做题前,回忆一下自由变量的概念闭包的结构

示例一:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

解析:

1.先看真正调用的是哪个函数 这里是 checkscope 调用这个函数返回它的内部函数 f ,这是符合一个闭包结构 (函数内部嵌套一个函数,且返回它

2.f 中的变量 scope 是一个自由变量,所以会顺着生成当前函数作用域的父作用域来寻找

3.向上寻找找到 checkscope 函数作用域,正好定义了一个 scope所以返回值是 local scope

示例二

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope(); 
foo();

解析:

1.foo()调用,这里的foo()相当于checkscope中的f。还是那句话,看是否是自由变量,然后顺着生成当前函数作用域的父作用域

这里输出的还是 local scope

做了两题是不是加深了概念,请记住,自由变量是顺着生成当前函数作用域的父作用域寻找

示例三:

var value = 1;
 
function foo() {
    console.log(value);
}
 
function bar() {
    var value = 2;
    foo();
}
 
bar();

看了这一题你就会理解为什么自由变量是顺着生成当前函数作用域的父作用域寻找

解析:

1.调用了barbar函数里又调用了foo ,再看foo可以得知输出value

2.可能有些同学会觉得选择 2 ,不过请记住自由变量是顺着生成当前函数作用域的父作用域寻找

3.生成foo函数的作用域是 全局作用域,这个自由变量value就是向全局作用域寻找 所以为什么会是1

示例四:

function F1(){
    var a=100;
    return function(){
        console.log(a);
    }
}
var f1=F1();
var a=200;
f1();

解析:

a是自由变量,自由变量是顺着生成当前函数作用域的父作用域寻找

所以答案是100

示例五:

function F2(){
    var b=100;
    return function(){
        console.log(b);
    }
}
var f2=F2();
function F3(fn){
    var b=300;
    fn();  //100
}
F3(f2);

解析:

bF2函数的匿名函数中是自由变量,还是那句话自由变量是顺着生成当前函数作用域的父作用域寻找

所以b为100

示例六:

var a = 1
function fn1(){  
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2   
  return fn3   
}
var fn = fn1() 
fn() 

解析:

1.fn1函数嵌套了两个函数f2f3,是两个闭包,且返回值是f3函数

2.fn调用其实就是调用f3函数,f3又调用了f2

3.此时来到fn2a是一个自由变量,还是那句话自由变量是顺着生成当前函数作用域的父作用域寻找

创建f2函数的作用域是 f1 则输出 2

在这里插入图片描述

示例七:

var a = 1        
function fn1(){
  function fn3(){  
    var a = 4
    fn2()        
  }
  var a = 2
  return fn3    
}

function fn2(){
  console.log(a)  
}
var fn = fn1()   
fn()

解析:

1.fn1嵌套了fn3这个函数,并且返回,调用fn其实就是调用了 fn3

2.fn3又调用了fn2,这是fn2中有自由变量a,遇到就还是那句话自由变量是顺着生成当前函数作用域的父作用域寻找

3.生成fn2函数的作用域是全局作用域,所以自由变量a就直接在全局中找,答案为1

在这里插入图片描述

示例八:

var a = 1
function fn1(){
  function fn3(){
    function fn2(){
      console.log(a)
    }
    fn2()
    var a = 4
  }      
  var a = 2
  return fn3
}
var fn = fn1()
fn()

解析:

1.fn1包含一个闭包fn3,fn3包含了一个闭包fn2,调用fn1其实就是调用了fn3

2.调用了fn3之后,里边又调用了fn2

3.fn2中有自由变量a ,一遇到直接那句话自由变量是顺着生成当前函数作用域的父作用域寻找

4.此时注意是,是先调用fn2再定义变量a,此时会有一个函数内部的变量提升。所以取值应该是undefined

函数内部变量提升应该变成这样的

var a = 1
function fn1(){
  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a //undefined
    fn2()
    a = 4 //先调用后赋值,所以是undefined
  }      
  var a = 2
  return fn3
}
var fn = fn1()
fn()

总结:

​ 遇到作用域作用域链及闭闭包的题目时候,不要慌张,记住以下几点就好了。

​ 1、先弄清楚到底调用的是哪个函数

​ 2、看看调用的哪个函数是否存在自由变量

​ 3、一遇到自由变量,直接那句话自由变量是顺着生成当前函数作用域的父作用域寻找

以上如有错误欢迎指正,一起学习 LOVE&&PEACE

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值