js进阶 --堆栈内存,变量提升02

132 阅读5分钟

作用域

  • 全局作用域
  • 私有作用域
  • es6块级作用域

全局作用域

当打开页面的时候,会提供一个供js代码执行的环境全局作用域,会提供一个最大的window对象

  • 全局变量:在全局作用域中声明的变量
  • 判断一个对象有没有某个属性:'属性名' in 对象名==返回true就是包含,返回false就是不包含==
var obj={name:li};
console.log("name" in obj);//true

全局变量和window的关系
  • 在全局作用域下声明的变量,相当于给window添加了一个属性,属性名是变量名,属性值是变量值
  • 在window身上的方法,我们可以直接省去window,如
window.alert(1);
alert(1);
//两者相等
带var与不带var的区别
  1. 都是在window添加对象
  2. 但是带var的先声明,到赋值代码行才赋值,不带var的是到代码行才声明赋值
  3. 带var的用delete window.属性名删不掉,不带var的可以删掉
console.log(a);//undefined
var a=12;
console.log(a);//12

console.log(b);//报错
b=13;
console.log(b);//如果不算报错不执行的话,13

"a" in window;//true
"b" in window;//true

delete window.a;//false删不掉
delete window.b;//true 删除了

//
function fn(x){
var n=2;
console.log(n);
m=9;
}
fn(1);//2
console.log(n);//报错
console.log(m);//9因为m不是var会向上查找,如果一直没有会给window

私有作用域

函数执行的时候形成的作用域就是私有的,保护里面的变量不受外界干扰

  • 私有变量:在私有作用域中声明的变量就是私有变量,函数的形参也是私有变量
  • 形参是在进入函数的时候先var一个形参
 function fn(x){
 //形参x也是私有变量
var m=3;//私有变量m
console.log(m);
console.log(x);
}
fn(2);
作用域链
  • 查找变量的时候,先看自己私有作用域内有没有,如果有就是自己私有的,如果没有,就想上一级作用域继续查找,如果上一级也没有,会继续向上查找,知道找到window为止,如果说window也没有,报错:变量名 is not defined(ES5)
function fn(b){
var x=2;
console.log(a);
}
fn(1);//报错
  • 赋值的时候(没带var的时候),先看自己私有作用域内有没有,如果有就是自己私有的,如果没有,就想上一级作用域继续查找,如果上一级也没有,会继续向上查找,直到找到window为止,如果说window也没有,赋值给window
function fn(x){
var n=2;
m=9;
}
fn(1);//2
console.log(m);//9因为m不是var会向上查找,如果一直没有会给window
function fn(x){
x=3;
}
fn(2);
console.log(x);//x is not defined 因为先var了一个形参x,后面的成为赋值了,就是私有变量

块级作用域

es6中用大括号包起来的, 外面无法访问

{
let a=12;
console.log(a);//12
}
console.log(a);//undefined

堆栈内存

栈内存

  1. 供js执行的环境
  2. 存储基本数据类型的值

堆内存

  1. 存储引用数据类型的值
  2. 对象,存的是键值对;函数存的是字符串

变量提升

  • 代码执行的时候,会首先形成一个供js执行的环境,接下来在代码自上而下执行之前会有一步操作"变量提升";会先把var的变量声明一下,把function函数声明加定义(赋值),不过此时的函数是存储在堆内存中的字符串,后面var的变量没到赋值代码行前输出都为undefined
console.log(a);//undefined
console.log(fn);//fn(x){y=2;console.log(x)}
var a =2;
function fn(x){y=2;console.log(x);};
console.log(a);//2
console.log(fn);//fn(x){y=2;console.log(x)}
  • 函数执行时开辟一个新的私有作用域(栈内存 ),会先进行形参赋值,然后进行变量提升 在这里插入图片描述
  • 只对等号左边的进行变量提升
console.log(fn);//undefined
console.log(fn(1,2));//报错
var fn=function (n,m){
    return n+m;
 }
console.log(fn(3,4));
  • 函数里面的return,return下面的代码本身是不执行的,但是可以进行变量提升return的代码不能进行变量提升
function fn(){
    console.log(f1);
    return function f1(){

    }
    function f2(){
        console.log("f2");
    }
}
fn();//报错

function fn(){
    console.log(f2);
    return function f1(){

    }
    function f2(){
        console.log("f2");
    }
}
fn();//f2(){console.log("f2")}
  • 对于变量名重复的,不进行二次变量提升,但会进行赋值覆盖
console.log(a);//a(){console.log(1)};
function a(){
console.log(1)};
var a=2;
console.log(a);//2
两个并排的括号代表什么
function fn(x){
    return function(y){
        return x+y;
    }
}
fn(1)(2);//3
//第一个括号是x的值,第二个括号内是y的值,且都执行

变量提升的特殊性

判断语句中,无论条件是否成立,都会进行变量提升

  • var 还是之前理解的只声明不定义
  • function 在老版本中声明+定义新版本中只声明不定义前提条件-->在判断语句中)
console.log(a); //undefined
//条件不成立,走不进去,没有进行赋值
if(1==2){
  var a=12;
}
console.log(a);//undefined
console.log(fn);// 在新版本浏览器中,判断条件中的function相当于只是声明(跟var一样),所以undefined
if(1==2){
   function fn(){
       console.log(1)
   }
}
console.log(fn); // undefined   条件不成立,只声明了,没赋值,(新版本浏览器)
//函数在老版本还是函数,新版本就是undefined
console.log(fn());//报错,因为此时的fn没有赋值,不是函数,不能执行,所以报错
console.log(a);//undefined
if(a in window){
    var a=100;
}
console.log(a);//100

自执行函数,在全局作用域中不进行变量提升,是私有作用域,所以内部进行的变量提升是私有变量

f=function(){
    return true;
};
g=function(){
    return false;
};
~function(){
//![]=false  []==false   0==0  
 if(g()&&[]==![]){ //报错g()is not defined
     f=function(){return false;};
     function g(){
         return true;
     }
 }
}();
console.log(f());
console.log(g());
//新版本判断时报错,老版本false false

新版本图解 在这里插入图片描述

老版本图解 在这里插入图片描述

console.log(fn);//undefined
if(1==1){
   console.log(fn);//fn(){console.log("ok")}
   function fn(){
       console.log("ok");
   }
}
console.log(fn)//fn(){console.log("ok")}

特殊情况,在判断语句中,如果出现function,后面再对此变量进行改值,改变的是私有的

var a=0;
    if(true){
    console.log(a);//a(){}
       a=1;
       function a(){}
       a=21;
       console.log(a);//21
    }
    console.log(a);//1