如何理解作用域
作用域就代表了某个变量合法的使用范围
作用域分为三种
1.函数作用域
2.全局作用域
3.ES6块级作用域
自由变量
一个变量在当前作用域没有被定义,但是被使用了
自由变量的查找
向上级作用域一层层的寻找
如果到了全局作用域都没有找到,则报错not defined
闭包的两个场景
闭包
作用域应用的一种特殊情况
一句话概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。
闭包的影响:变量会常住内存,得不到释放。闭包不要乱用
两个场景:
1.函数作为参数被传递
//函数作为返回值
function create() {
const a = 100
return function () {
console.log(a)
}
}
const fn = create()
const a = 200
fn() // 100
2.函数作为参数被传递
// 函数作为参数被传递
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) // 100
两个都打印100
总结:所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
不是在执行的地方!!!
this几种不同的使用场景:
注意:this只有在执行时才能确认值,定义时不能
①作为普通函数被调用
function fn(){
console.log(this)
}
fn() //window
this指向window
②使用call apply bind
fn1.call({x:100}) //{x:100}
const fn2 = fn1.bind({x:200})
fn2() //{x:200}
call能改变this
this指向所传入的对象
bind和call不同点在于必须重新赋值一个新函数
call和apply的区别参数不同:fn.call(this,p1,p2,p3)
fn.apply(this,arguments)
③作为对象方法被调用
const zs = {
name: '张三',
say: function() {
console.log(this)
}
}
zs.say() //当前对象
this指向当前对象
④在class方法中被调用
class people {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
console.log(this)
}
}
const zs1 = new people("张三",18)
zs1.say() //{name:'张三',age:18}
this指向当前实例
⑤箭头函数
const zs = {
name: '张三',
say: function() {
console.log(this)
},
aa: function() {
setTimeout(() => {
console.log(this)
})
}
}
zs.aa() //指向当前对象
箭头函数this取上级作用域的this
const zs = {
name: '张三',
say: function() {
console.log(this)
},
aa: function() {
setTimeout(function (){
console.log(this) //this===window
})
}
}
settimeout是直接执行的函数,和普通函数一样,this指向window
闭包经典面试题
创建10个a标签,点击弹出对应数字
方法1:ES6块级作用域
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
alert(i)
})
document.body.appendChild(a)
}
方法2:ES5写法,自执行函数
for (var i = 0; i < 10; i++) {
(function(i){
var a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
alert(i)
})
document.body.appendChild(a)
})(i)
}
1.块级作用域相当于循环结束时候形成了10个块,点击a标签,则弹出对应数字
2.错误写法,错误原因是因为for循环执行很快,等到点击执行方法时候,a标签已经变成10了,所以无论点击哪个a标签,都会弹出10
闭包实际应用
1.闭包隐藏数据,只提供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
c.data会报错,未定义
2.封装变量,收敛权限
功能:判断用户是不是首次登录
// 封装变量,收敛权限
function isFirstLoad() {
let _List = [] // 闭包中的数据,被隐藏,不被外界访问
return {
if(_List.indexOf(id) >= 0){
return false
}else{
_List.push(id)
return true
}
}
}
//使用
var firstLoad = isFirstLoad()
firstLoad (1) //true
firstLoad (1) //false
firstLoad (2) //true
在isFirstLoad函数外,根本无法修改_List的值