js变量提升
![](https://img-blog.csdnimg.cn/img_convert/ec1c7f181ae6e38dad283b9ac653d562.png)
堆栈内存 以及 基本&引用数据类型
基本数据类型&引用数据类型
浏览器加载我们js时候,首先会形成一个全局作用域window提供一个代码自上而下执行的环境;
-
引用类型会在作用域外 新开辟一块内存空间(有一个16进制地址),键值对依次存储到这个内存中;基本数据类型是直接在作用域开了一个位置就存了。
![](https://img-blog.csdnimg.cn/img_convert/b3798fe7ca5b7a1e566458d7442f6a8d.png)
![](https://img-blog.csdnimg.cn/img_convert/674d1ca43db9b4d519679062fc969c54.png)
-
想让代码执行就得有一个执行的环境,所以函数执行首先都会形成一个私有的作用域。
作用域:
-
提供代码自上而下执行的环境
-
存储基本数据类型,所有基本数据类型都是直接在该作用域开辟一个位置
栈内存:即作用域
堆内存:存储引用数据类型
释放内存,让其不被占用,arr=null,
![](https://img-blog.csdnimg.cn/img_convert/2fa6d1646598254baf72af6c60a03f23.png)
变量提升机制
在代码自上而下执行之前,浏览器会把带var或者function关键字的进行“声明”或“定义”,这就叫变量提升
变量提升只发生在当前作用域
私有作用域的变量提升和全局基本一致。私有作用域下的这些变量也就是私有变量,那我们就把保护私有作用域下的私有变量叫做闭包。
![](https://img-blog.csdnimg.cn/img_convert/24664b80374b169d7131ba9ca1158900.png)
带var和不带var
全局的var变量和window属性存在映射关系
不加var:添加了window的一个属性
![](https://img-blog.csdnimg.cn/img_convert/cacb8d6c64f2ba67373d595981702886.png)
私有作用域
带var:私有变量,跟外界没有关系
不带var:就会向上级作用域查找,“作用域链”机制
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
function fn(){
b=13;
console.log(b) // 13
}
fn();
console.log(b) // 13
console.log(window.b) // 13
提升的一些细节问题
函数表达式和普通函数的区别
普通函数:用function关键词来处理的
函数表达式:只对函数左边进行变量提升
console.log(fn);
fn() // Uncaught TypeError: fn is not a function
sum()
var fn=function(){
console.log(1)
}
function sum(){
console.log(2)
}
条件判断下的变量提升
当前作用域下,不管条件是否成立,都要进行变量提升
console.log(a) // undefined
if(1===2){
var a = 2;
}
console.log(a); // undefined
// 另一种情况
console.log(a)
if('a' in window){
var a = 100;
}
console.log(a);
function的一道题
-
全局下没有变量提升(自执行没有名字所以也没有变量提升)。
window.f = ...
window.g = ...
-
自执行函数部分:
创建一个函数并且把它执行了。只要是函数执行,肯定会形成一个私有作用域。
变量提升:带function的也只剩声明了g,但没有定义。私有作用域中声明一个变量是私有的,跟全局作用域是没有关系的。
-
[] -> []==false -> 0==0
f = function(){ return true; };
g = function(){ return false; };
(function(){
console.log('---');
console.log(window.g);
function g(){ return true; };
if(g() && [] == ![]){
console.log('panduan');
f = function(){ return false; }
function g(){ return true; };
}
})();
console.log(f());
console.log(g());
![](https://img-blog.csdnimg.cn/img_convert/87105fea3212958a82333850e34f1465.png)
这里还有块级作用域
console.log(fn);
// if(1===2) 那么结果又不一样 undefined
if(1===1){
console.log(fn);
function fn(){
console.log('ok');
}
}
console.log(fn);
但是这里又不是完整的块级作用域,因为这个fn还是全局的。
重名问题的处理
重名时会重新赋值不会重新声明
fn(); // 4
function fn(){ console.log(1) };
fn(); // 4
function fn(){ console.log(2) };
fn(); // 4
var fn = 100;
fn(); // Uncaught TypeError: fn is not a function
function fn(){ console.log(3) };
fn(); // Uncaught TypeError: fn is not a function
function fn(){ console.log(4) };
fn();
题外话
![](https://img-blog.csdnimg.cn/img_convert/beae19b4b5ab00777542379f08ef0778.png)
let不存在变量提升
let/const不存在变量提升。
let a=6;
console.log(a); // 6
console.log(window.a); // undefined 不存在映射机制的
语法检测:
es6机制下,代码执行之前首先会做语法检测。
-
重复定义的会报错
-
并且它说,这些定义的变量在正式定义之前是不能使用的。
// 这个例子很好
// 全局 a,b,fn
// fn执行形成一个私有作用域:let a,所以用a只能用在创建a的后面;b不是let,这里没有出现b,往上级去找
let a=10,b=10;
let fn=function(){
// console.log(a,b);
let a = b=20;
console.log(a,b);
}
fn();
console.log(a,b);
![](https://img-blog.csdnimg.cn/img_convert/be5eae7443fa3135cf4c43409ac91400.png)
a=3;
let a=8;
console.log(a);
![](https://img-blog.csdnimg.cn/img_convert/0137697eb128ba29f1cc59f335eee89f.png)
暂时性死区
总结:
首先先形成一个栈内存全局作用域,或私有作用域私有栈内存,在私有作用域下也会先验证它是不是私有变量,如果是的话就是自己的,如果不是则向上级作用域查找。
代码执行之前会做很多事情:
老规范会对变量进行提前声明或者定义,则按照变量提升机制走,新规范重复检测机制。
let 会形成一个块级私有作用域,同上面函数作用域一样,在定义之前不能使用。
// 题目
var a=12;
if(true){
console.log(a); // 同理,这里使用也报错
let a=13;
console.log(a);
}
console.log(a);
![](https://img-blog.csdnimg.cn/img_convert/c6dd845fe269e59c33640f77199bd5f0.png)
// 这是typeof的一个bug
console.log(typeof a); // undefined
![](https://img-blog.csdnimg.cn/img_convert/fd942bebbe7641e68fbac486a6d7d0b4.png)