-
变量提升(预解释)
- 在 js 代码执行之前,会对所有带 var 和 function 的变量进行声明与定义(赋值)这就是变量提升
- 对于带 var 的进行提前声明,不赋值(变量声明不赋值的时候,变量会有默认值为undefined),只是让浏览器知道有这么个变量;
- 对于带 function 进行提前声明,并且赋值定义为函数本身,变量提升结束后,代码会从上到下执行.
- 当执行到 var 声明的变量时, 此时才会对变量进行赋值,即在此之后变量才会代表这个值本身,在此之前表现为 undefined;
- 而当执行到 function 函数名时,因为之前变量提升阶段就已经完成对变量的赋值所以函数会直接执行编辑好的脚本.
console.log(a); // undefined
var a = 1;
console.log(a); // 1
console.log(fun); // 函数本身,在变量提升的时候已经赋值了
fun();// 祝大家2022年幸福康健
function fun(){
console.log("祝大家2022年幸福康健")
};
js 运行机制及大概原理:
js运行的环境(栈内存):作用域
- 作用域是js的运行的环境,另外的一个功能就是保存基本数据类型.在js中作用域分为:
- 全局作用域: 当页面打开时,首先形成一个全局作用域,执行全局中的代码,全局作用域是 window;
- 私有作用域(函数作用域): 当函数执行时,会形成一个函数作用域,这个作用域用来保存函数中的基本数据同时执行函数代码;
- 块级作用域: 类似于私有作用域生效与{}内, 且是 let 声明的变量
js运行过程
- 在js代码执行前,浏览器会开辟一个全局作用域,然后执行变量提升,完成变量提升操作后,代码从上到下执行;
- 当执行时,遇到基本数据类型,就会在作用域中存储该数据;
- 如果遇到引用数据类型,浏览器就会分配一个堆内存空间,然后就把引用数据类型中的内容存储在堆内存中,接下来把这个堆内存空间的地址赋值给变量(此时这个地址是存储在当前作用域中即栈内存)
- 遇到函数执行时,会经历以下这几步:
- 浏览器开辟一个私有作用域;
- 形参赋值,把执行时的实参赋值给函数的形参变量;
- 私有作用域变量提升
- 函数代码从上到下执行
私有变量和全局变量
- 全局变量: 在window中声明的变量(全局作用域)
- 私有变量: 函数的形参以及在函数私有作用域中声明的变量, {中用 let 声明的变量}
预解释只会发生在当前作用域中,如果函数不执行,函数中的变量也不会进行变量提升
console.log(a); // 报错
function fn(){
var a = 1;
}
console.log(a); // 报错
重复声明问题
- 同名变量只会声明一次,代表的值是最后一次赋值的值
var a = 1;
var a = 2;
var a = 2;
var a = 4;
console.log(a); // 4
function fn(){
console.log(1)
}
function fn(){
console.log(2)
}
function fn(){
console.log(3)
}
fn(); // 3
- 变量提升时,function 声明的变量优先级要高于普通变量
console.log(fn);// fn 函数本身
function fn(){
console.log("fn:谁能有我级别高")
}
var fn = 2;
console.log(fn); // 2
- 如果变量名和函数名同名, 在执行到变量的赋值语句之前,这个名字就是代表着函数,但是执行过变量的赋值语句之后代表的就是赋值的这个值本身了
function fn(){
console.log("fn:谁能有我级别高")
}
console.log(fn); // 函数本身
var fn = 2;
fn(); // 报错
变量提升的细节问题
- 等号右边的不会进行变量提升,即使右侧是函数也不会进行变量提升
a(); // 报错
var a = function () {
console.log("宝子你来了")
};
console.log(a1); // 报错: a1 is not defined
console.log(a2); // undefined
var a2 = funtcion a1(){
comsole.log("宝子你又来了")
}
- 条件语句中的变量不管条件成立与否都会参与当前作用域中的变量提升
console.log(a); // undefined
if(NaN === Number("来了啊宝子")){
var a = "来了呗";
}
console.log(a); // undefined 条件没生效 赋值语句没执行
- 函数中,return 下面的代码虽然不执行,但是会进行函数作用域中的变量提升
function getAB(a,b){
console.log(n);
fn();
return a + b;
var n = 123;
function fn(){
console.log("房价太高了!!!")
}
}
var c = getAB(1,9);
- 函数的返回值不参与变量提升,return 右侧的不会参与变量提升
function getLove(){
console.log(fn);
return function fn(){
consloe.log("return 右侧的返回值不参与私有作用域内的变量提升")
}
}
getLove();
带var 声明与其他不带var变量声明方式的区别
- 在全局中,用var和function声明的变量,也相当于给window上添加一个同名属性
- 全局作用域是一个内置对象 window
console.log(a); // undefined
var a = 1;
console.log(b); // 报错
b = 2; // 不带var的不会进行变量提升
console.log(b); // 2
let const 暂时性死区问题
console.log(a); // 报错 a is not defined
let a = 1;
ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。ps:权威的解释很明白了 es6.ruanyifeng.com/#docs/let
自我感悟: 所以并不是lat const没有变量提升而是在js线程没有执行到变量声明赋值之前因为有暂时性死区的限制你是拿不到值的自然也就报错了
作用域链
- js 中作用域有:
- 全局作用域
- 私有作用域
- 块级作用域
var a = 1; // 储存在全局作用域
function fn(){
console.log(a); // 作用域向上查找 1
};
fn();
function fn2(){
console.log(x); // x is not defined x 在函数的私有作用域中查找不到
};
fn2();
function fn3(){
var x = 16
};
if(false){
let a = 3; // 块级作用域中不暴露外界
}
console.log(a); // 1 let a = 3;块级作用域中不暴露外界
作用域链: 变量的查找机制
当在作用域中查找一个变量的时候,先看当前作用域中是否声明过这个变量,如果声明过,就使用变量,如果没有声明过,那么就去上一级作用域中查找(上级作用域就是函数声明所在的作用域),找到就使用该变量,如果还没有找到就在继续向上查找.一直找到window 为止,如果这次的查找是赋值该变量,那么就相当于给window的上面添加一个属性,但如果是引用类型的变量,就会报错.
function getVariable(){
var a = 200;
return function son(){
console.log(a); // 200 该作用域没有a这个变量向上查找 上级作用域有a的变量 拿来使用
console.log(b); // is not defined
}
}
var fn = getVariable();
fn();
上级作用域的确定:
- 如何查找上级作用域,就看这个函数是在哪里定义的.
function getVariable(){ // getVariable是在全局作用域定义的那getVariable的上级作用域就是window
var a = 200;
return function son(){ // son 这个函数是在getVariable函数中定义的那son的上级作用域就是getVariable
console.log(a); // 200 该作用域没有a这个变量向上查找 上级作用域有a的变量 拿来使用
console.log(b); // is not defined
}
}
var fn = getVariable();
fn();