什么是作用域
作用域决定了代码区块中变量和其他资源的可见性,**作用域就是一个独立的独立的地盘,让变量不外泄.暴露出去,作用域最大的用处就是隔离变量,不同作用域下变量名不会冲突
作用域可以分为
1.全局作用域,
2.私有作用域
3.es6块级作用域
全局作用域
当打开页面的时候,会提供一个供js代码执行的环境,全局作用域,会默认提供一个最大的window对象。
【全局变量】:在全局作用域中声明的变量就是全局变量,其中下面的a 和fn 就是全局变量
【全局变量和window关系】 在全局作用域中声明的变量:var、function(除去es6的),相当于给window上添加了属性,属性名就是变量名,属性值就是变量值
调用window 下的方法时,可以省去window不写:
window.alert("1");
alert("1");
window.Number("1");
Number("1");
var a=3; // a是全局变量
function fn(){ // fn 是全局变量
var num=3;
function f1(){
}
}
console.log(window.a);
console.log(window.fn);
所有未定义直接赋值的变量自动声明为拥有全局变量
所有window对象的属性拥有全局作用域
所有window对象拥有全局作用域
全局作用域的弊端:如果我们写了很多行代码,变量定义没有用函数包括,那么他们就全部在全局作用域中,这样会污染全局命名空间容易引起命名冲突
函数作用域(私有作用域)
【私有变量】:
- 形参
- 在私有作用域中声明的变量就是私有变量。
在私有作用域外面,去访问私有变量的话,是访问不到的,但是在私有作用域里面可以访问到外面的变量。
var a = 66;
function fn() {
var x = 1;
var y = 3;
console.log(a);//66
return x + y;
}
console.log(x);// x is not defined
fn();
块语句(大括号{}中间的语句)如Switch和if条件语句或for和while循环语句,不像函数它们不会创建新的作用域,在块语句级作用域中定义的变量将保留在他们已经存在的作用域中
es6块级作用域
先来看一下es6中的let
let不存在变量提升
console.log(a);//Uncaught ReferenceError: a is not defined
let a=2;
阻断了和window的关系
let a=2;
console.log(window.a);// undefined
不能重复声明
es6中没有变量提升,但是有一个自我检测的一个机制,在代码自上而下执行前,会先进行检测,看是否有重复声明的变量,如果有的话,就先报错。
let a=2;
console.log(a);
var a=3; // 不能进行重复的声明:Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(3);
暂时性死区
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
我们来看块级作用域
块级作用域可以通过新增命令let和const声明,所声明的变量在指定块的作用域外无法访问,块级作用域在如下情况被创建
1.在一个函数内部
2.在一个代码块(由一对花括号包裹)内部
我们来看循环中的块级作用域 更好的了解一下块级作用域
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[6]();//10
上面代码中,变量i是var声明的,在全局范围内有效,全局中只有有一个变量i每次循环i的值都会发生变化,而循环内的i指向的是全局的i,也就是说所有数组a的成员里面的i指向的都是同一个i导致运行时输出的结果是最后一轮i的值 也就是10
再来看用let声明的 声明的变量仅在块级作用域内部有效 最后输出的结果是6
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[6]();// 6
变量i是let声明的,当前的i只在本轮循环有效,所以每次循环你的i其实都是一个新的变量所以最后输出的结果是6
思考:如果每一轮循环的变量i都是重复声明的那它怎么知道上一轮循环的值,从而计算出本轮循环的值?
这是因为js引擎内部会记住上一轮循环的值 ,初始化本轮的变量i时 就在上一轮循环的基础上进行计算
什么是作用域链
在私有作用域中,如果没有定义这个私有变量,就会向上一级作用域进行查找,如果都没有,继续进行查找,直到查找到window为止。如果是赋值,声明的时候用了var、function 就是给GO添加(也是给window)添加,不带任何关键字的,相当于省了window,也是给Go添加,如果声明的时候用了let、const 直接给VO添加。
上级作用域:
当前函数执行,形成一个私有作用域A,这个A的上级作用域是谁,跟它在哪执行无关,跟它在哪定义(创建)有关系,在哪创建,它的上级作用域就是谁
简而言之:上级作用域和函数在哪执行无关,和函数在哪定义有关
练习题
console.log(a,b);// undefined undefined
var a=12,
b=12;
function fn(){
console.log(a,b);// undefined 12
var a=b=13;
console.log(a,b);// 13 13
}
fn();
console.log(a,b);// 12 13
[解析]
代码自上而下执行之前会把var 和function进行变量和提升 var只声明不定义function声明和定义一起完成 ,
代码自上而下执行 此时console.log(a,b) 都是undefined,提升阶段函数fn已经不在执行直接跳过
在调用fn fn执行 会形成私有作用域普通函数执行
普通函数执行
- 会形成全新的私有上下文
- 初始化自己的作用域链{自己的上下文,上级上下文'}
- 初始化this
- 初始化arguments
- 形参赋值
- 变量提升
- 代码执行
- 默认返回undefined
此时函数内部的a提升到自己作用域的顶部还是只声明不定义 console.log(a,b)a 是undefined b会根据作用域链找到全局 b为12
var a=b=13; 此时b不带var不是私有变量会向上一级作用域进行查找 如果还没有找到,一直继续向上查找直到找到window这种机制叫做作用域链查找, console.log(a,b)此时a是私有13,b不是私有变量,并将全局的b修改为13
console.log(a,b) 打印的是全局的ab 现在是 a12 b13
-------------------------------------完结--------------------------------------------------
-------------------------接收大佬们的批改和指点,欢迎留言-------------------------------------