zoukankan      html  css  js  c++  java
  • js

    首先,咱们通常被"执行上下文","执行上下文环境","上下文环境","执行上下文栈"这些名词搞混。那我们一一来揭秘这些名字的含义。

    这一块一直比较晦涩难懂,还是需要仔细去斟酌斟酌。

    什么是执行上下文(也叫做“执行上下文环境”,“上下文环境”)?

    咱们还是先看代码。

    console.log(a)  // undefined
    var a = 100
    
    fn('bella')  // 'bella' 20
    function fn(name) {
        age = 20
        console.log(name, age)
        var age
    }
    
    console.log(b); // 这里报错
    // Uncaught ReferenceError: b is not defined
    

    第一个console输出 undefined,说明浏览器在执行console.log(a)的时候,已经知道a的存在的,但是不知道a的值。

    第二个fn("bella")输出 "bella" 20,说明浏览器在执行的时候已经知道fn函数了,并且执行了

    第三个console报错,b is noe defined。说明没有找到b

    那么可以看出来,浏览器在执行之前做了一些准备工作。

    那做了些什么准备工作:

    • 全局上下文环境: 变量定义,函数声明
    • 函数上下文环境(函数内部):变量定义,函数声明,this,arguments

    下面这个例子很多地方都用来讲解执行上下文和上下文栈。

     1 // 这是一个压栈出栈的过程--执行上下文栈
     2 let a = 10; // 1、进入全局上下文环境
     3 let fn; 
     4 let bar = function(x) {
     5     let b = 5
     6     fn(x + b) // 3、进入fn函数上下文环境
     7 };
     8 fn = function(y) {
     9     let c = 5
    10     console.log(y + c)
    11 }
    12 
    13 bar(10) // 2、 进入bar函数上下文

     这里引出了执行上下文栈的概念,上下文栈就是压栈和出栈的过程

    1.在代码执行之前,首先创建全局上下文环境

        // 全局上下文环境
        a: undefined
        fn: undefined
        bar: undefined,
    this: window

    2.然后执行代码,在代码执行到12之前,全局上下文中的变量在执行中被赋值

        // 全局上下文环境
        a: 10
        fn: function
        bar: function,
    this: window

    然后执行13行代码,调用bar函数,会创建一个新的执行上下文环境。并将这个bar上下文环境压栈,并设置为活动状态

       // bar函数上下文环境
        b: undefined
        x: 10
        arguments: [10]
    this: window

    3.然后执行到第6行代码,调用fn的时候,会创建一个新的执行上下文。并将这个fn上下文环境压栈,并设置为活动状态。

    // fn函数上下文环境
    c: undefined
    y: 15
    arguments: [15]
    this: window

    4.fn执行完毕后,调用fn函数生成的fn上下文环境出栈,销毁。然后bar出栈销毁。然后全局上下文出栈销毁

    理解完了执行上下文,再看看this

    相信都知道这句话,谁调用函数,this就指向谁。那么我们理解下this:

        var a = {
            name: 'A',
            fn: function() {
                console.log(this.name)
            }
        }
        a.fn() // this === a
        a.fn.call({
            name: 'B'
        }) // this === {name: 'B'}
        var fn1 = a.fn
        fn1() // this === window

    this: this的值只有在执行的时候才能确认,定义的时候不能确认。因为this是执行上下文的一部分,而执行上下文需要再代码执行之前确定。

    this执行会有不同,主要集中在这几个场景中:
    1. 作为构造函数执行,构造函数中
    2. 作为对象属性执行,上述代码中a.fn()
    3. 作为普通函数执行,上述代码中fn1()
    4. 用于call apply bind,上述代码中a.fn.call()

    作用域


    ES6之前没有块级作用域,除了全局作用域,函数会创建自己的作用域。
    作用域在函数定义的时候已经确定了,不是在函数调用确定(区别于执行上下文环境,this是执行上下文环境中的)
    作用域只是一个“地盘”,其中没有变量。变量是通过作用域对应的执行上下文环境中的变量对象来实现的。所以作用域是静态的,而执行上下文是动态的
    有闭包存在的时候,一个作用域存在两个上下文环境也是有的。
    也就是说,作用域只是用于划分你在这个作用域里面定义的变量的有效范围,出了这个范围就无效
     

    作用域链

    函数在定义的时候就确定了函数体内部自由变量的作用域
    自由变量:比如a,在fn作用域使用,但是并没有在fn作用域定义。这就是自由变量
    let a = 100
    function fn() {
        let b = 20
        function bar() {
            console.log(a + b) // a是自由变量
        }
        return bar
    }
     
    let x = fn(), b = 200
    x()
     
    那么自由变量是如何得到的?这就引出了作用域链
    bar要取得a的值,就要在bar函数的作用域中取值,如果没有,就往上找,找到fn作用域内,也没有定义a,继续往上找,就找到全局作用域,找到就结束了。这就是作用域链
     
     
    讲完这些,我们再通过一个例子来理解闭包
    1 function F1() {
    2     var a = 100
    3     return function () {
    4         console.log(a)
    5     }
    6 }
    7 var f1 = F1()
    8 var a = 200
    9 f1()
    自由变量将从作用域链中去寻找,但是 依据的是函数定义时的作用域链,而不是函数执行时,以上这个例子就是闭包。
    怎么理解依据的是函数定义时的作用域链,而不是函数执行时这句话?
     
    调用第9行之后
    如果按照执行时,就输出的时200
    但是作用域都是在定义时就生成了,所以f1回去再定义function的作用域去找,因此输出100.
     
    理解闭包之后,我们就看看闭包的主要场景:
    • 函数作为返回值,上面的例子就是
    • 函数作为参数传递
  • 相关阅读:
    类似直播点赞动画(出现一颗心缓缓升起然后消失)
    进入App弹出提示框
    iOS NSString 截取字符串(根据索引截取)
    刷新tableView 保持头部视图 不变
    截取一段字符串中,两个指定字符串中间的字符串
    ios 导航栏透明, 上下滑动 导航栏 颜色渐变
    ios 自定义键盘的return键以及键盘的其他一些属性
    百度地图通过坐标定位 自己的位置显示小圆点 (精度圈是否显示根据自己喜好) 上图
    百度地图定位 (直接上图上代码)
    iOS 对H5加载html的数据的一些基础设置
  • 原文地址:https://www.cnblogs.com/thonrt/p/10333581.html
Copyright © 2011-2022 走看看