变量提升的概念
当栈内存(作用域)形成,JS 代码自上而下执行之前
,浏览器首先会把所有带 var / function
关键字开头的进行提前声明或者定义,这种预先处理机制称为“变量提升”。
- 声明(declare):
var a
(默认undefined
)- 定义(defined):
a = 12
(定义就是赋值操作)- 变量提升只发生在当前作用域(如开始加载页面时只对全局作用域下的进行提升,因此此时函数中存储的都是字符串而已)
- 在全局作用域下声明的函数或者变量是“全局变量”,在私有作用域下声明的变量是“私有变量”(带var/function的才是声明)
- 浏览器做过的事情不会重复第二遍,就是当代码执行遇到创建函数这部分代码时候直接跳过(因为在提升阶段就完成赋值操作)
变量提升阶段:
- 带
var
是只提升声明未定义(给个默认值undefined
) - 带
function
是提升声明和定义都完成了
console.log(a); //=> undefined,如果后面都没有声明,那么就会报错
var a = 12;
b(); //=> 1,可以直接使用,如果后面没有定义,那么会报错
function b() {
console.log(1);
}
因为用
function
关键字声明的函数 ,在变量提升阶段已经赋好值了,所以可以在 JS 文件中的任意位置调用这个函数
- 示例
var a = 12;
var b = a;
b = 13;
console.log(a);
/* var ary1 = [12,23];
var ary2 = ary1;
ary2.push(100);
console.log(ary1);
*/
function sum(){
var total = null;
for(var i = 0;i < arguments.length; i++){
var item = arguments[i];
item = parseFloat(item);
!isNaN(item) ? total += item : null;
}
return total;
}
console.log(sum(12,23,'34','AA'))
运行机制
变量的声明会被自动移到函数或者全局代码的最顶上。移动的仅仅是declarations
,变量的定义并不会随之提升,如下代码:
var date = new Date();
function fn(){
console.log(date);
if(true){
var date = 'hello';
}
}
fn();
结果并不是date的toString方法返回的结果,而是undefined,因为以上代码等价于:
// 变量声明提升
var date;
date = new Date();
function fn(){
// 变量声明提升,但是此时未定义变量的值
var date;
console.log(date);
if(true){
date = "hello";
}
}
fn();
但是在变量提升中还存在着一些特殊情况,因为在ES5中,变量声明、函数声明都会被提升,这就衍生出很多值得辨析的问题。
在ES6中,function *, let, class, const
也会被提升,但是提升机制又与变量提升、函数提升有所区别
四大原则
- 所有声明都会被提升到对应作用域的顶上
- 同一个变量声明只进行一次,其他重复声明会被JS解析忽略
- 函数声明进行提升时会连带函数定义一起提升
- 遵循前三项原则多多动手写等价转换,就一定不会出错
在ECMAScript5中,JS只有两类作用域:全局作用域、函数作用域
。
- 全局作用域:全局对象的作用域,在代码的任何地方都可访问,但有时会被函数作用域覆盖
- 函数作用域:作用于整个函数范围内,不管到底是在函数中的何处进行声明
// 全局变量
var i = 100;
// 函数声明,outer是一个外部函数
function outer(){
// 访问全局变量
console.log(i); // 100
// 函数声明,inner是一个内部函数
function inner(){
// 内部函数的内部进行了变量提升,也就是第二部分叙述的内容
console.log(i); // undefined
// 这里的i是局部变量,作用域仅在函数内
var i = 1;
// 局部变量覆盖全局变量,或者说是函数作用域覆盖全局作用域
console.log(i); // 1
}
inner();
// 这里的i是全局变量
console.log(i); // 100
}
outer();
定义变量时,如果不写var
,那么就会相当于声明了一个全局变量,作用域为全局作用域;否则声明的是局部变量,作用域为函数作用域。在以上代码段中,第一行的var i = 0
是全局变量,虽然它添加var
,但是在全局范畴中声明,而且不在函数范围内,因此效果等同于i = 0
。但是在JS编程中应该尽力避免不加var
,即使真的需要全局变量,也应该在最外层作用域中使用var
声明。
网友评论