1.什么是作用域?
作用域就是变量与函数的可访问范围,即作用域控制着函数与变量的可见性和生命周期
1.全局作用域
任何地方都能访问到的对象拥有全局作用域
1.1.函数外面定义的变量拥有全局作用域,全局变量拥有全局作用域,网页中所有脚本和函数均可使用。全局变量在页面关闭后销毁
var n = 2; function fn(){ var a = 1; returrn a; } console.log(fn()); //1 console.log(n); // 2 console.log(a); //报错error
1.2.未定义直接赋值的变量自动声明为全局变量拥有全局作用域
var n = 2; function fn(){ a = 1; returrn a; } console.log(fn()); //1 console.log(n); // 2 console.log(a); //1
1.3.window对象的属性拥有全局作用域
HTML中,全局变量是window对象,所有数据变量都属于window对象
//此处可使用 window.carName function myFunction() { carName = "Volvo"; }
2.局部作用域
局部作用域一般只在固定的代码片段内能访问到,最常见的例如函数内部,所以也称为函数作用域
变量在函数内声明,变量为局部作用域
局部变量:只能在函数内访问
// 此处不能调用 carName 变量 function myFunction() { var carName = "Volvo"; // 函数内可调用 carName 变量 }
因局部变量只作用于函数内,所以不同的函数内可以使用相同名称的变量名
局部变量在函数开始执行时创建,函数执行完成后局部变量会自动销毁
3.ES6的块级作用域
ES5只有全局作用域和函数作用域,没有块级作用域,会带来以下问题
3.1.变量提升导致内层变量可能会覆盖外层变量
var i = 5; function func(){ console.log(i); if(true){ var i = 6; } } func(); //undefined
3.2.用来计数的循环变量泄露为全局变量
for(var i = 0;i<10;i++){ console.log(i) } console.log(i); //10
ES6引入了块级作用域,明确允许在块级作用域中申明函数,let和const命令都涉及块级作用域
块级作用域允许声明函数只在使用大括号的情况下成立,如果未使用大括号报错
if (true) { function func1() {} // 不报错 } if (true) function func2() {} // 报错
作用域链
当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链
1.当执行函数时,总是先从函数内部寻找局部变量
2.如果内部找不到(函数的局部作用域没有),则会像创建函数的作用域(声明函数的作用域)寻找,依次向上
var a = 1; function fn(){ var a = 10; function fn1(){ var a = 20; console.log(a); //20 } function fn2(){ console.log(a); //10 } fn1(); fn2(); } fn(); console.log(a); //1
当执行fn1()时,创建函数fn1的执行环境,并将该对象置于链表开头,然后将fn的调用对象放在第二位,最后是全局对象,作用域链的链表结构是fn1>fn>window,从链表的开头寻找变量a,即从fn1函数内部找变量a,找到了结果是20.
同样,当执行fn2()时,创建函数fn2的执行环境,并将该对象置于链表开头,然后将fn的调用对象放在第二位,最后是全局对象,作用域链的链表结构是fn2>fn>window,从链表的开头寻找变量a,即从fn2函数内部找变量a,找不到,于是从fn内部找变量a,找到了结果是10
最后再最外层打印变量a,直接从变量a的作用域即全局作用域内寻找,结果为1
4.闭包
简单来说,闭包就是函数外部能读取到函数内部的变量
优点:闭包可以形成独立的空间,永久的保存局部变量
缺点:保存中间值的状态缺点是造成内存泄漏,因为闭包中的局部变量永远不会被回收
function f1(){ var n = 999; nAdd = function(){ n += 1; } function f2(){ console.log(n) } return f2; } var result = f1(); result(); //999 nAdd(); //执行 n+=1; result(); //1000