JavaScript变量作用域
变量提升概念
1、变量提升
在js中,用var声明的变量,解释器都会把声明提升到它所在作用域的顶端去执行,到我们代码所在的位置来赋值
代码示例1:
使用var声明的变量会被提升到所有代码执行之前
console.log(x) var x = 123;
解析器执行时,代码实际是下面这种:
var x; console.log(x);//所以此时代码不汇报 x is not defined,而是输入undefined x = 123;
2、函数提升(变量提升的变种)
同理,用var声明的函数,解释器都会把声明提升到它所在作用域的顶端去执行,到我们代码所在的位置来赋值
console.log(func); func();//执行时报错! var func = function(){ console.log('我是一个函数!') }
上线那段代码执行时会报错,原因如下,解释器执行改代码的逻辑顺序:
var func; console.log(func);//输出undefined func();//因为此时func还未被实际赋值为函数(函数也是对象嘛,也就是说还没有被实例化),只是个undefined,undefined对象不是个函数, 执行报错 func = function(){ console.log('我是一个var关键字声明的函数!') } 运行结果为: undefined func();//执行时报错! ^ TypeError: func is not a function
3、避免变量/函数提升方式
3.1 使用 let、const声明
代码所在位置及声明处,此代码未执行时,变量不会被声明
3.2 函数使用function声明
console.log(func); func(); function func(){ console.log('我是一个function关键字声明的函数!'); } 输出结果: [Function: func] 我是一个function关键字声明的函数!
解释器在执行上面代码时,逻辑顺序如下:
//会把function声明的函数对象提升到所有代码之前 function func(){ console.log('我是一个function关键字声明的函数!'); } console.log(func); func();
顶层对象(window/global)
1、作用范围
整个浏览器(或者说js解释器)的代码都在该对象作用范围下
-
例如:document、history、navigator等
-
有很多默认的属性和方法
2、给window/global添加属性
注意:顶层对象也是个对象,js中的Object可以随便添加删除属性,这一点不像java
-
在全局作用域中使用var、function,都是在给window、global添加属性
-
function关键字定义的可以说添加方法,怎么说都行
-
无论在任何地方,不使用任何关键字,直接给变量赋值,都相当于给顶层对象添加了属性
3、代码解释
name = 'wugb'; if(true){ age = 18; } console.log(name); console.log(age); 输出结果: wugb 18
为什么在if语句中定义的age变量也能正常输出呢?因为在解释器中,上面的代码是这样的
//浏览器 window.name = 'wugb'; if(true){ window.age = 18; } console.log(window.name); console.log(window.age);
即:如果未使用任何关键字进行声明,则变量直接被window对象添加为自身属性,在之后使用到该对象时,如果根据 块-->函数-->全局这条链都找不到定义的话,就会直接从顶层对象中尝试获取该属性值
4、避免变量作用域混乱
4.1、避免或禁止使用无关键字定义变量的方式
4.2、如果需要给顶层对象赋值,直接 window.变量名 = '变量值',简单直观
一、ES5及之前版本
1、定义方式:
1.1 var key = value;
1.2 直接 key = value;
var name = 'wugb'; 或 name = 'wugb'; 区别见2.2
2、var - 主动声明唯一关键字!
在老版本中,js定义变量只有一个关键字 ---- var
2.1.默认作用域:函数之内
注意:window是浏览器默认对象,也可以说是函数
2.2 在哪定义
①、全局作用域
无论用不用var,都会自动成为window对象的属性
区别:
-- var关键字会导致变量提升,哪怕没赋值都可以用,只不过这会儿自身是undefined
-- 而未用任何关键字定义的变量 没办法提前使用,使用则报错 xxx is not defined,因为它没有变量提升这一重buff么
②、函数(注意:对象也是个函数)
整个函数体内都可以用,原因:变量提升
因为js解释器在执行js代码时,会把var修饰的变量都提升到作用域任何代码前去声明,就导致了该变量对函数内的任何一个地方都可见
showMyName(); function showMyName(){ console.log(name); if(true){ var name = 'wugb'; } if(true){ console.log(name); } } 输入结果让人大吃一惊: undefined wugb
问题1:为何执行showMyName()时,没有报错showMyName is not undefined?
答:因为function修饰的方法会变量提升,所以其方法的声明在代码执行之前
问题2:为何第一次打印name时,没有报错name is not undefined?
答:因为var定义的变量会出现变量提升现象,所以早在第一行输出代码前,name 已经被声明了,只是还未被赋值
问题3:为何第二次打印name时,明明已经不在同一个代码块中了,也不报错呢?在java中肯定报错了!
答:同样是因为变量提升现象,name已经被声明,且对整个函数内的任何地方都可见,所以第二次输出不会报错,甚至还被赋值过。
//实际在js解释器中,上述代码是这么执行的 function showMyName(){ var name; console.log(name); if(true){ name = 'wugb'; } if(true){ console.log(name); } } showMyName();
1、将showMyName函数的声明提升到最上方
-- 此时该方法已经可以执行了
2、将函数内的var修饰的name变量提升到最上方声明
-- 此时name属性已经在整个函数内都可见了,只要是函数内的代码,都可以访问到name变量
3、所以:第一次打印:undefined,第二次打印:wugb
2.3 直接key = value
相当于给顶层对象加属性,不建议这种方式
二、ES6及之后版本
var --- 和之前一样,整个作用域内变量提升,从而整个作用域(全局/函数)内有效
let、const ---定义变量或常量,没有变量提升,所以是块级作用域,和java一样,就不多说了
直接key = value,和之前版本一样,都是给顶层对象添加/修改属性,也不多说了
总结:
有变量提升特性的关键字(var),作用域就是全局/整个函数内
没有变量提升特性的关键字(let/const),作用域就是一个代码块,和java一样
其实就是相当于js多了个变量提升的概念,其他的和java都一样