作用域
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。
细分作用域
- 全局作用域:在全局作用域定义的变量可以在任何地方被访问。
- 函数作用域: 某一函数拥有的作用域
- 块级作用域:在某一区域块能访问的作用域
var b=10;
function test(){
var a=10;
}
console.log(b==a)
//ReferenceError: a is not defined
复制代码
可见,函数外面是不能访问函数内的变量,a所在的作用域与b所在的作用域不同。b处于全局作用域中,a处于函数作用域。
var b=10
function test(){
var a=10;
console.log(b==a)
}
test()//true
复制代码
因为b是全局作用域,所以即使是在函数中也能访问到。
if(ture){
var a=10
}
console.log(a)//10
复制代码
在块级作用域定义的a变量,怎么能在全局中访问?
- 这是因为用 var 定义的变量并不具有块级作用域
如果块级作用域无效很容易造成变量的命名冲突。为了解决该问题, es6 之后JavaScript 提供了 let、const 关键字来让变量具有块级作用域。
if(ture){
let a=10
}
console.log(a)//ReferenceError: a is not defined
复制代码
小提升
先看题目、求输出结果
var arr = [];
for(var i = 0;i < 10;i++){
arr[i] = function(){
console.log(i)
};
}
for(var j = 0;j < 10;j++){
arr[j]()
}
复制代码
大家觉得输出得是0-9,还是10个10呢?
我们先分析之后再求结果。
先引入异步的概念,异步:
指的是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
这里面的函数执行就是异步操作。
因为我们得输出i。所以我们先看看哪里是执行输出i的代码
在第一个for循环中,函数作用域中是没有i变量的,遵循作用域的原则,向上寻找变量 i,当这个函数需要执行的时候,因为是异步操作此时的i已经for循环完了值为10.所以当执行函数时输出的i=10.
结果就是10个10.
显然我们希望得到的是0-9,那我们该如何改进呢?
var arr = [];
for(let i = 0;i < 10;i++){
arr[i] = function(){
console.log(i)
};
}
for(var j = 0;j < 10;j++){
arr[j]()
}
复制代码
把var变成let就行了,因为let定义的变量会独立创建块级作用域,每次let都会将此时i定义到此时块级作用域,所以输出结果为0-9.
- 即然上面的函数是异步操作,那我们可以让它立即执行。
var arr = [];
for(let i = 0;i < 10;i++){
arr[i] = (function(n){
console.log(n)
})(i)
}
//0-9
复制代码
闭包
什么叫闭包
闭包就是能够读取其他函数内部变量的函数.
???
简单的理解
当一个函数被保存到外部是,产生闭包。
客官,您点的小李子来了:
如果老师布置作业,需要两个人一起完成,其中你需要写个功能,计算运行某个函数的次数。
大部分人都能想到,下面的方法:
var count = 0
function test(){
count ++
}
test()
复制代码
显而易见,当test函数执行,count就加一。不幸的是,你搭伴的同学也定义了个变量count,变量名冲突了,这时候该怎么办?有没有一个方法能保留住函数内部的某个变量。
- 看代码
function add(){
var num=0
function a(){
num++
console.log(num)
}
return a
}
var result =add()
result()//1
复制代码
我们先解析下改代码:
- 先调用 add函数,定义变量result 保存其返回值。
- add函数 的返回值也是一个函数对象:a函数。所以我们才可以在外部调用 a函数。
- 执行a函数,num++,得先找到num,找找找,a函数作用域没有,往上找,add函数中找到了。但add函数已经执行了呀?当一个函数执行完后,它本身作用域也不复存在了,那为什么还能得到num?
当一函数执行结束后,回收机制会将其回收。
但add函数执行后,回收机制刚想将其回收,函数会对回收机制说,大哥虽然我是执行完了,但result有访问权限,并且调用了我返回的函数a,我也不知道什么时候result执行a函数,所以我并没有执行结束。所以仍然能在add函数中找到num。
就好比,我之前很有钱,并将一部分钱买了基金。有一天我赌博把钱全输了,那你能说我破产了嘛?显然没有,我还有一些基金,虽然我不知道这基金到底能赚多少,但我确实没破产。
其实这就是闭包的概念,a函数属于add函数作用域,但却保存到了全局中。
闭包的缺陷
1. 内存泄漏:内存可用空间变小了,引擎可用空间不够用
2. 浏览器负荷加重,需要更多的资源去执行
闭包的缺陷很好理解,上面例子中,因为回收机制不能将函数add回收,所以add函数就会长时间占用内存可用空间,导致引擎可用空间不够用,浏览器负荷加重,需要更多的资源去执行。
闭包的优点
存在即合理,即然有闭包,那就有它存在的好处:
1. 实现共有变量
上面例子就将num变成了共有变量
**2. 缓存 **
先看代码
function book(){
var bo = '灌篮高手'
var a= new Object
a.readbook=function read(){
console.log("我在看 "+bo)
}
a.buybook=function buy(){
bo='三国演义'
console.log('我买了'+bo)
}
return a
}
var me=book()
me.readbook()
me.buybook()
me.readbook()
//我在看 灌篮高手
//我买了三国演义
//我在看 三国演义
复制代码
将bo进行了缓存
3. 实现封装
var call=function(){
var name ='范佐'
var me=function(){
console.log('我的名字是:'+name)
}
return me
}
var callname= call()
callname()
复制代码
闭包使得方法实例能够正常调用到方法外部的变量,当call函数执行后,call返回的函数实例
callname仍能访问到上一级函数中的name。相当于callname调用了call里面的变量,即callname于name绑定在一起了。闭包将外部变量和方法实例封装。
好了,本次的学习分享就到这了?。若文章有错,还请各位点名批评!!!
下次见?♂️