作用域是指程序源代码中定义变量的区域。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。也就是说
函数的作用域在函数定义的时候确定的
。动态作用域相对于
this
来说的
全局作用域
函数外部声明的变量,作用域为全局的,所有函数均可访问
函数作用域
在js函数内部声明的变量,仅在函数内部起到局部作用
块级作用域
es6中新增的let以及const,let和const声明的对象作用域就是块级作用域,仅在声明对象所在的代码块中作用
在作用域中嵌套作用域,形成作用域链
不在当前作用域定义的变量
函数能访问自己作用域,不在自己所在作用域调用,就形成闭包
var a = 10
function foo(){
console.log(a)
function bar(){
var a = 1
foo()
bar()
以上foo调用其实就是一个闭包,不太明显,看下面
function foo(){
var count = 0
return function(){
console.log(count)
var b = foo()
b()
b函数能够记住并访问foo函数变量count
,这就是闭包,其实是作用域的一种特殊形式;
自执行函数IIFE
自执行函数表达式,其实是匿名函数自调用;优点是:封装变量;减少对全局变量污染;相当于一个独立的作用域;因为自己调用自己,它本身并不是闭包;但是它可以作为一闭包作用域,提供给定义在内部的函数消费
(function(){
var a = 100
setTimeout(function(){
console.log(a)
},0)
探讨for循环
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i)
上面for循环会打印五个5;并不是预期中的0,1,2,3,4;为什么会这样?
仔细想一下;setTimeout是异步执行;当for循环结束后开始执行延迟函数回调;这个时候开始查找i;由于i其实是定义在全局作用域的共享变量;所以最终查找的是同一个i,也就是5;
所以我们需要一个独立的作用域,且每次迭代都要一个不重复的闭包作用域来存储不同的i;这样我们访问的i就是不同的;
使用自执行函数试一下
for(var i = 0; i < 5; i++){
(function(j){
setTimeout(function(){
console.log(j)
})(i)
此时使用自执行函数可以在每次迭代都生成一个新的作用域;相当于把每次迭代的i都存到j中;异步回调执行访问的j都是所在自调用函数作用域内部的j;所以实现对i的存储;
使用let
for(let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i)
let也可是实现对i的存储;ES6中for循环的let有个特殊的行为;使用let声明的i,不会只声明一次,每次迭代都会声明,而且会自动记住上次迭代后的i值来重新初始化,每次i不重复;也就是每次迭代都会形成一个块级作用域来供延迟函数访问,这样块作用域与闭包完美结合;let简直为for循环量身定制的;终于可以愉快地写代码了
另外for括号中let声明的i与{}
中不属于同一个块;也就是说{}
作为子作用域,也可以声明i;父子作用域互不影响
for(let i = 0; i < 5; i++){
let i = 10
setTimeout(function(){
console.log(i)
同样的也可以在{}
中直接存储i
for(var i = 0; i < 5; i++){
let j = i
setTimeout(function(){
console.log(j)
isfff
An unknown developer