一、作用域和闭包
1. this
的不同应用场景,如何取值?
答案: this取什么样的值是在函数执行的时候决定的,不是在函数定义的时候决定的。
解析:
作用域和自由变量
- 作用域分为:全局作用域、函数作用域、块级作用域(ES6新增)。js
if (true) { let a = 3 } console.log(a) //报错,这称为块级作用域
- 自由变量:
- 一个变量在当前作用域没有定义,但被使用了。
- 向上级作用域,一层一层依次寻找,直到找到为止。
- 如果到全局作用域都没找到,则报错
xx is not defined
- 作用域分为:全局作用域、函数作用域、块级作用域(ES6新增)。
闭包
- 闭包是作用域应用的特殊情况,有两种表现:一种是函数作为参数被传递;一种是函数作为返回值被返回。
- 对于下述的两种情况:关键点在于,
所有自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!
js// 函数作为返回值 function create() { let a = 100 return function() { console.log(a); } } const fn = create() a = 200 fn() // 100
js// 函数作为参数 function print(fn) { let b = 200 fn() } b = 100 function fn() { console.log(b); } print(fn) // 100
this
注意:
this取什么样的值是在函数执行的时候决定的,不是在函数定义的时候决定的。
- 作为普通函数
- 使用call、apply、bindjs
function fn1(){ console.log(this); } fn1() // window fn1.call({ x: 200 }); // { x: 100 } const fn2 = fn1.bind({ x: 200 }); fn2(); // { x: 200 }
- 作为对象方法被调用
- 在class方法中调用
- 箭头函数js
const zhangsan = { name: '张三', sayHi() { // this 即当前对象 console.log(this); }, wait() { setTimeout(() => { // this 即当前对象 console.log(this); }) } } zhangsan.sayHi() zhangsan.wait()
2. 手写bind
函数
- 第一步,将函数的参数从arguments这个伪数组转化成数组
- 第二步,推出数组的第一项作为this对象
- 第三步,用变量self指向调用bind1的参数
- 最后,返回一个匿名函数,匿名函数的返回值使用调用apply
js
// arguments对象是所有(非箭头)函数中都可用的局部变量。
// 模拟bind
Function.prototype.bind1 = function() {
// 将arguments对象转化成数组
// const args = Array.prototype.slice.call(arguments)
const args = [...arguments]
// 获取 this (数组第一项)
const t = args.shift()
//self指向调用bind1的函数
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);
3. 实际开发中闭包的应用场景,举例说明
答案: 隐藏数据,只提供api
js
// 闭包隐藏数据,只提供API
function createCache() {
const data = {} //闭包中的数据,被隐藏,不被外界访问
return {
set(key, val) {
data[key] = val
},
get(key) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'));