了解闭包,var和let const的区别

292 阅读5分钟

一、声明变量var 和不写var的区别

带var的时候就是声明变量,不带var的时候,没有变量提升


在全局作用域下,在var还是不带var 都是给GO添加了一个属性(也相当于给window),属性名就是此变量,属性值就是变量值

console.log(a); //undefined 带var 先声明变量但是不赋值 所以在var a上面输出a是undefined,如果在上面输出b 因为b没有var声明不能变量提升,会直接报错 b is not defined
var a=3;
b=6;
console.log(window.a);//3
console.log("a" in window);//true 判断window里面有没有a这个属性
delete window.a;
delete window.b;

判断一个对象到底有没有一个属性:用“属性名”in 对象,如果返回值是false 就说明不存在,true说明存在

obj={"name":"lili"}; 

console.log(“name” in obj )// true  说明name就是obj的属性
console.log("age" in obj)//false    说明age 不是obj的属性

二、let const 和var 区别

/**
 * let const && var 的区别
*/
var num = 12; 
//let vs const
/*
  letconst声明的都是变量,只不过const声明的变量,
  不允许重新指向其他的值(和值关联的指针是不能改变的)
  const 不能直接写const m,必须给一个初始值,不然会报错
*/
const m = {
  name: "cxh"
}
m.name = "zcf"
console.log(m)
//let vs var 
/*
 1、在全局上下文中,基于var声明的变量是直接放在GO(window)中,
 而基于let声明的变量是放在VO(G)中 的,和GO没关系
 2、var存在的变量提升,let不存在变量提升
 3、var 允许重复声明,而let是不允许的[而且在词法分析阶段就过不去]
 4let会产生块级上下文,var不会
 5、关于暂时性死区问题:typeof 检测一个未声明的变量,
 不会报错,结果是undefined 在let声明前使用就会报错
*/
console.log(m);//m is not defined
console.log(typeof m)//"undefined"
console.log(typeof a)//报错 未声明前不能使用
let a = 10

暂时性死区:

ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

console.log(a);// ReferenceError: a is not defined
let a=2;

三、作用域链

在私有作用域中,如果没有定义这个私有变量,就会向上一级作用域进行查找,

如果都没有,继续进行查找,直到查找到window为止,

如果此变量声明的时候用了 let、const,就相当于给VO添加一个这样的变量,

如果声明的时候用了var、function 或者压根没声明就相当于给GO添加了一个属性

(也相当于给window添加了一个属性)

console.log(a,b);
var a=12,
b=12;
function fn(){
    console.log(a,b);
    var a=b=13;
    console.log(a,b);
}
fn();
console.log(a,b);

image.png

函数底层运行机制

1.私有上下文

函数执行,就会形成一个全新的私有上下文,然后进栈执行私有上下文,首先会有一个AO(私有变量对象-->形参变量和函数体内声明过的变量),来储存私有变量

代码执行之前会做那些事:

  • 初始作用域链:<自己的上下文(执行产生的),函数的作用域(创建时候声明)>
  • 初始this
  • 初始arguments
  • 形参赋值
  • 变量提升
  • 代码执行

四、上级作用域:

上级作用域:当前函数执行,形成一个私有作用域A,在这个A的上级作用域是谁,跟它在哪执行无关,跟它在哪定义(创建)有关系,在哪创建,它的上级作用域就是谁

简而言之:上级作用域和函数在哪执行无关,和函数在哪定义有关

var n=10;
function fn(){
    var n=20;
    function f(){
        n++;
        console.log(n);//21 22 230
    }
    f();
    return f;
}
var x=fn();
x();
x();
console.log(n);//10

image.png

堆栈内存小科普

1.js中的内存非为:堆内存和栈内存

【堆内存】:只要用来存储引用数据类型的值(对象存的是键值对,函数存的是字符串) 【栈内存】:供js运行的环境(函数执行),存基本数据类型

2.堆栈内存释放问题

每次给变量存值或执行函数的时候都会占用内存空间,如果这样一直下去,日积月累,电脑总会装不下,所以内存是需要释放的

  • 堆内存的释放: 常见的浏览器释放方式主要有以下两种:
  • 谷歌浏览器是标记方式,每隔一段时间就会检测一下当前作用域中的内存,是否被占用,如果没有被占用,就会释放掉。
  • ie和火狐等浏览器是采用计数方法,当前作用域中如果一个空间地址被占用一次,就会累加一,如果减少一次占用就会减1,直到0的时候,说明已经没有被占用了,就释放了。

 堆内存释放:让所有引用这个堆内存的变量赋值为null,堆内存地址不在被占用,浏览器在空闲的时候就会把堆内存 释放

  • 栈内存释放:
  • 销毁:全局栈内存,只有当页面关闭的时候才会被释放
  • 销毁:函数执行完成,一般情况下都会被销毁掉
  • 不销毁的情况: 被嵌套的函数内部引用外部函数的形参或者变量
var function fn(){
    var num=2;
    return function(){
       console.log(num);
    };
};
var f=fn();
  • 不立即销毁,(函数执行完毕之后才会销毁):
function fn(x){
   return function(y){
      return x+y;
   }
}

fn(1)(2);

五、闭包

闭包声明时候产生? 1.函数嵌套函数 2.被嵌套函数内部引用了外部函数的形参或变量 3.外部函数被调用 有点:1.延长了局部变量的生命周期 2.可以通过闭包实现一些高级一点啊功能

1.闭包的作用

  • 保护:保护里面的私有变量不受外界干扰
  • 保存:形成不销毁的作用域,可以把里面的变量保存下来,
// 实现一个功能:调用一次函数就 + 1
function fn() {
  var num = 0
  num++
  return num
}
fn()
fn()
fn()
var num = fn()
console.log(num)