• JavaScript基础知识: 作用域和闭包


    一、作用域

    在这里插入图片描述

    1.1 块级作用域

    // ES6 块级作用域
    if (true) {
    	let x = 10
    }
    console.log(x) // 报错
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2 自由变量

    • 一个变量在当前作用域没有定义,但被使用了;
    • 向上级作用域,一层一层依次寻找,直到找到为止;
    • 如果到全局作用域都没找到,报错 xx is not undefined;

    二、闭包

    作用域应用的特殊情况,有两种表现:

    1. 函数作为参数被传递
    2. 函数作为返回值被返回
    // 函数作为返回值
    function create() {
      const a = 100
      return function() {
        console.log('a: ', a)
      }
    }
    
    let fn = create()
    const a = 200
    fn()  // 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // 函数作为参数
    function print(fn) {
      let a = 200
      fn()
    }
    let a = 100
    function fn() {
      console.log(a)
    }
    print(fn) // 100
    // 闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找
    //      不是在执行的地方!!!
    // 其实也不仅仅是闭包,是所有的自由变量的查找。。。。。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、this

    this有这么几种使用场景,不是多复杂,而是使用场景较多。

    1. 作为普通函数
    2. 使用 call apply bind
    3. 作为对象方法去调用
    4. 在class方法中调用
    5. 箭头函数

    this 取什么值,是在函数执行的时候确定,而不是定义的时候。

    function fn1() {
      console.log(this)
    }
    
    fn1() // window
    
    fn1.call({x: 100}) // {x: 100}
    const fn2 = fn1.bind({x: 200})
    fn2() // {x: 200}
    
    // bind和call有点不一样,bind返回的是一个函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    const zhangsan = {
      name: '张三',
      sayHi() {
        console.log(this) // this即当前对象
      },
      wait() {
        setTimeout(function() {
          // setTimeout触发的函数执行,作为普通函数执行,window
          console.log(this) // this 是 window
        }, 10)
      }
    }
    zhangsan.sayHi()
    zhangsan.wait()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    上图中的箭头函数:箭头函数的this,永远取的是它上级作用域的this。

    在这里插入图片描述
    在这里插入图片描述

    三、bind

    bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值,例如,f.bind(obj),实际上可以理解为obj.f(),这时,f函数体内的this自然指向的是obj

    在这里插入图片描述
    手动实现bind
    在这里插入图片描述
    可以看到bind属于原型链中的
    在这里插入图片描述
    在这里插入图片描述

    四、call

    使用call方法,可以编写能够在不同对象上使用的方法。
    call方法可以用来调用所有者对象作为参数的方法。
    通过call,能够使用属于另外一个对象的方法。
    比如下面,调用person对象的fullName方法,用于person1对象。

    const person = {
      fullName: function() {
        return this.firstName + ' ' + this.lastName
      }
    }
    
    const person1 = {
      firstName: 'Bill',
      lastName: 'Gates'
    }
    
    const person2 = {
      firstName: 'Steve',
      lastName: 'Jobs'
    }
    
    const res = person.fullName.call(person1)
    console.log(res) // Bill Gates
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    五、实际开发中的闭包

    数据隐藏,只提供API,数据内部自己管理

    // 闭包隐藏数据,只提供API
    function createCache() {
      const data = {} // 闭包中的数据,被隐藏,不被外界访问
      return {
        set: function(key, val) {
          data[key] = val
        },
        get: function(key) {
          return data[key]
        }
      }
    }
    
    const c = createCache()
    c.set('a', 100)
    console.log(c.get('a')) // 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    六、原型中的this

    class People {
      constructor(name) {
        this.name = name
      }
    
      eat() {
        console.log(`${this.name} eat sth`)
      }
    }
    
    class Student extends People {
      constructor(name, number) {
        // super:把名字给父类People,父类会帮我们处理,调用父类构造函数,但是学号number是学生特有的,自己处理
        super(name)
        this.number = number
      }
      sayHi() {
        console.log(`姓名: ${this.name}  学号: ${this.number}`)
      }
    }
    const xialuo = new Student('夏洛', 10010)
    console.log(xialuo) // Student { name: '夏洛', number: 10010 }
    console.log('........')
    console.log(xialuo.sayHi()) // 姓名: 夏洛  学号: 10010
    console.log(xialuo.__proto__.sayHi) // 会正确打印sayHi函数体,没问题
    
    // [下行运行结果] 姓名: undefined  学号: undefined【this指向的问题,上面的this是xialuo,此行的this是xialuo.__proto__】
    console.log(xialuo.__proto__.sayHi())
    console.log(xialuo.__proto__.name) // undefined
    console.log(xialuo.__proto__.number) // undefined
    console.log(xialuo.__proto__.sayHi.call(xialuo)) // Student { name: '夏洛', number: 10010 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    汽车智能制造中的RFID技术在供应链生产管理中的应用
    Python接入企业微信 - 推送信息到内部群里
    Spring Boot日志配置及输出
    sklearn机器学习——day08
    LaTeX中的积分符号
    sendfile数据copy流程
    JXLS 导出多sheet,带页眉页脚
    LeetCode54题:螺旋矩阵(python3)
    人体神经元与胶质关系图,神经元和神经胶质关系
    转化率的催化剂:网站客服机器人如何推动企业销售?
  • 原文地址:https://blog.csdn.net/GY_U_YG/article/details/125286585