一、声明变量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
/*
let 和 const声明的都是变量,只不过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是不允许的[而且在词法分析阶段就过不去]
4、let会产生块级上下文,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);
函数底层运行机制
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
堆栈内存小科普
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)