(六)作用域和闭包的基础详细讲解

老规矩,我们这一知识点今天的讲解还是通过先看题,看了题后去思考该题考察的知识点,然后再讲解知识点,最后又回归到我们的例题中

题目

  • this的不同应用场景,如何取值?
  • 手写bind函数,改变this指向的方法之一
  • 实际开发中闭包的应用场景,举例说明

知识点

  • 作用域和自由变量
  • 闭包
  • this

作用域

一个变量的合法使用范围

image.png

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

自由变量

  • 一个变量在当前作用域没有定义,但被使用了,就是没有声明
  • 向上级作用域,一层一层依次寻找,直到找到为止
  • 如果全局都没找到,会报错说没有这个变量

闭包

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

  • 函数作为参数被传递
  • 函数作为返回值被返回

从定义的时候去寻找变量取值,向上寻找~
image.png
不熟悉的小伙伴,还是自己手敲一下

所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方

this

应用场景:

  • 作为普通函数
  • 使用call apply bind (bind会返回一个新的函数)
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数

this的取值是在执行上下文执行/函数执行的时候确定的

普通函数调用,是看调用方
function fn1 (){
    console.log(this)
}
fn1()  // window

call改变了调用函数
fn1.call({x:100}) // {x:100}
bind返回了一个新函数,需要重新调用才会执行
const fn2 = fn1.bind({ x:200 })
fn2()  // {x:200}
复制代码
const zhangsan = {
    name:"张三",
    sayHi(){
        console.log(this)
    },
    wait(){
        setTimeout(function(){
          
            console.log(this)
        })
    }
}
zhangsan.sayHi() //this是当前对象
zhangsan.wait() // 因为是setTimeout触发的执行,this指向的是window
复制代码
const zhangsan = {
    name:"张三",
    sayHi(){
        console.log(this)
    },
    waitAgain(){
        setTimeout(()=>{
            // this即当前对象
            console.log(this)
        })
    }
}
zhangsan.waitAgain() // 箭头函数内的this指向父级,这里指向当前对象
复制代码
class People {
    constructor(name) {
        this.name = name
        this.age = 20 // this指向当前实例
    }
    sayHi() {
        console.log(this)// this指向当前实例
    }
}
const zhangsan = new People('张三')
zhangsan.sayHi()  // zhangsan对象
复制代码

手写bind函数

我们先看一般我们bind的使用方法和使用效果

function fn1 (a, b, c){
    console.log('this', this)
    console.log(a, b, c)
    return 'this is fn1'
}
const fn2 = fn1.bind({x:100},10,20,30) 
const res = fn2()
console.log(res) 
// this {x:100}  10 20 30    this is fn1
fn1.hasOwnProperty('bind') // false
fn1._proto_  // f(){...}
fn1._proto_ === Function.prototype 
// 说明Function.prototype上有bind,call,apply这些API
复制代码
// 模拟bind 自己手写bind函数,可以在原型上增加这个方法
Function.prototype.bind1 = function(){
    // bind方法不确定内部会给它传多少个参数,所以我们将参数拆解成数组
    // arguments可以拿到所有的参数,它是一个列表,但不是一个数组
    // 将arguments拆解成数组
    const args = Array.prototype.slice.call(arguments) 
    // 获取 this (第一个参数,也就是数组第一项)
    // const arr  = [1,2,3,4,5]   arr.shift() => 1 且 arr = [2,3,4,5]改变了原数组
    const t = args.shift()
    // fn1.bind1(...)中的fn1
    const self = this
    // 返回一个函数
    return function (){
       return self.apply(t,args)
    }
}
const fn2 = fn1.bind1({x:100},10,20,30) 
const res = fn2()
console.log(res)

// 手写 apply call bind 都要会 
复制代码

闭包的应用

  • 隐藏数据
  • 做一个cache工具
// 闭包的隐藏,这里只提供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)//只能通过set修改数据或者新增数据
console.log(c.get('a'))//只能通过get拿到数据

复制代码
// 创建10个<a>, 点击弹出的序号
let a
for (let i = 0, i< 10;i++){
 // let i 是for内绑定的i,块级作用域,每循环一次就会新增一个块级作用域
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click',function(e){
    // click事件不会立马执行
        e.preventDefault()
        alert(i)
    })
    document.body.appendChild(a)
}
复制代码

小结

  • 作用域和自由变量
  • 闭包:两种常见方式&自由变量查找规则
  • this,执行的时候确定的;使用场景
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享