快速搞定前端Javascript面试课程笔记(三)—JS基础—作用域和闭包

152 阅读3分钟

课程来源:coding.imooc.com/learn/list/…

题目

  • this的不同应用场景,如何取值?
  • 手写bind函数
  • 实际开发中闭包的应用场景,举例说明
  • 场景题目
// 创建10个`<a>`标签,点击的时候弹出来对应的序号
let i,a
for (i = 0; i < 10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function (e) {
        e.preventDefault()
        alert(i)
    })
    document.body.appendChild(a)
}

知识点

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

一、作用域和自由变量

juejin-6

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)
// ES6块级作用域
if (true) {
    let x = 100
}
console.log(x)  // 会报错

自由变量

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

二、闭包

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

  • 函数作为参数被传递
  • 函数作为返回值被返回
// 函数作为返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}
const fn = create()
const a = 200
fn()  // 100


// 函数作为参数
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn)  // 100

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

三、this

五种this使用场景:

  • 作为普通函数被调用
  • 使用call apply bind
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数

this在各个场景中取什么样的值,是在函数执行时候确定,不是在函数定义时候确定的。

function fn1() {
    console.log(this)
}
fn1()  // window

fn1.call({ x: 100 })  // { x: 100 }

const fn2 = fn1.bind({ x: 200 })
fn2()  // { x: 200 }
const zhangsan = {
    name: '张三'
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    wait() {
        setTimeout(function() {
            // this === window
            console.log(this)
        })
    }
}

const zhangsan = {
    name: '张三'
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    waitAgain() {
        setTimeout(() => {
            // this 即当前对象
            console.log(this)
        })
    }
}

class People {
    constructor(name) {
        this.name = name
        this.age = age
    }
    sayHi() {
        console.log(this)
    }
}
const zhangsan = new People('张三')
zhangsan.sayHi()  // zhangsan 对象

四、问题补充解答

手写bind函数

// 模拟 bind
Function.prototype.bind1 = function () {
    // 将参数解析为数组
    const args = Array.prototype.slice.call(arguments)
    	// 将arguments作为Array.prototype.slice的this赋值
    // 获取 this (数组第一项)
    const t = args.shift()
    
    // fn1.bind(...) 中的 fn1
    const self = this
    
    // 返回一个函数
    return function () {
        return self.apply(t, args)
    }
}

// 使用场景
function fn1(a, b, c) {
    console.log('this', this)
    console.log(a, b, c)
    return 'this is fn1'
}
const fn2 = fn1.bind1({x: 100}, 10, 20, 30)
const res = fn2()
console.log(res)

实际开发中闭包的应用

  • 隐藏数据
  • 如做一个简单的cache工具
// 闭包隐藏数据,只提供API
function createCache() {
    const data = {}  // 闭包中的数据,被隐藏,不被外界访问
    return {
        set: function (key, val) {
            data[key] = val;
        },
        get: function (key) {
            return data[key]
        }
    }
}

const c = createCathe()
c.set('a', 100)
console.log( c.get('a') )

创建10个<a>标签,点击的时候弹出来对应的序号

let a
for (let i = 0; i < 10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function (e) {
        e.preventDefault()
        alert(i)
    })
    document.body.appendChild(a)
}

————————————————————————————————————————