前端JS面试题-基础-作用域和闭包
写在前面:本文内容主要根据慕课网双越老师的付费课程“一天时间迅速准备前端面试 快速构建初级前端知识体系 ”进行编写,主要是为了自己在面试前总结学习,欢迎留言指教。
本文包括如下内容:
每一部分包括题目和知识点两部分。
作用域和闭包
题目
- this的不同应用场景,如何取值
- 手写bind函数
- 实际开发中闭包的应用场景,举例说明
- 场景题
1. this的不同应用场景,如何取值
有5种应用场景,如下:
- 作为普通函数——返回window
- 使用 call apply bind——传入什么绑定什么
- 作为对象方法被调用——返回对象本身
- 在class方法中调用——当前实例本身
- 箭头函数——上级作用域
2. 手写bind函数
bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其原本的参数。
//模拟 bind
Function.prototype.bind1 = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
//获取 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)
console.log(fn2())
// 结果
this {x: 100}
10 20 30
this is fn1
3. 实际开发中闭包的应用场景,举例说明
- 函数作为返回值,以cache缓存为例:
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'))
- 函数作为参数被传递,以setTimeout定时器为例:
function fn() {
alert()
}
const func = fn()
// 第一个参数是一个函数,或者是一段执行的js代码,第二参数是第一个参数执行的时间间隔。
setTimeout(func, 1000)
被面试官问到什么是闭包,最好的回答就是把两个 demo 写一遍,然后说出闭包的使用场景【cache缓存 setTimeout定时器 异步操作 等】。不用说什么概念。
4. 场景题
这个代码打印出来的序号都是 10,应该在for里定义let i,这样需要都是递增的。
知识点
1. 作用域
- 全局作用域
- 函数作用域
- 块级作用域(ES6新增)【大括号内是一个块级作用域】
2. 自由变量【当前作用域未定义的变量】
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域,一层一层一次寻找,直至找到为止
- 如果到全局作用域都没找到,则报错 xx is not defined
所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!
3. 闭包
作用域引用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
4. this
有5种应用场景,如下:
- 作为普通函数——返回window
- 使用 call apply bind——传入什么绑定什么
- 作为对象方法被调用——返回对象本身
- 在class方法中调用——当前实例本身
- 箭头函数——上级作用域
// 普通函数:返回window
function fn1() {
console.log(this)
}
fn1() //window
// call apply bind:传入什么绑定什么
fn1.call({x: 100}) //{x: 100}
const fn2 = fn1.bind({x:200})
fn2() //{x:200}
// 作为对象方法被调用:返回对象本身
const zhangsan = {
name: '张三',
sayhi(){
console.log(this)
}
}
zhangsan.sayhi() //{name: "张三", sayhi: ?}
// 在class方法中调用:返回实例本身
class People{
constructor(name){
this.name = name
}
sayhi(){
console.log(this)
}
}
const zhangsan = new People('张三')
zhangsan.sayhi() //People {name: "张三"}
// 箭头函数:上级作用域
const zhangsan = {
name: '张三',
wait(){
setTimeout(()=>{
console.log(this)
})
},
waitAgain(){
setTimeout(function () {
console.log(this)
})
}
// 两个函数进行对比
}
zhangsan.wait() //{name: "张三", wait: ?, waitAgain: ?}
zhangsan.waitAgain() //window
this取什么值,是在函数执行的时候确认的,不是在函数定义的时候确认的