为了账号安全,请及时绑定邮箱和手机立即绑定

作用域闭包

标签:
JavaScript

如何理解作用域

作用域就代表了某个变量合法的使用范围
图片描述
作用域分为三种
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的值

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消