作用域【scope】又叫 【栈内存】
全局作用域:
window
私有作用域:
函数执行形成私有作用域
块级作用域:
使用let 创建变量存在块级作用域
作用域链
:当前作用域代码执行的时候遇到一个变量我们首先看一下它是否属于私有变量
,
如果是当前作用域的私有变量,那么以后再私有作用域中再遇到这个变量都是操作私有的(闭包:私有作用域保护私有变量不受外界干扰
);
如果不是私有
变量,向其上级作用域查找
,也不是上级作用域私有的,继续向上查找,一直找到window
全局作用域为止,我们把这种向上一级级查找的机制叫做作用域链
;
如果全局下有这个变量,操作的就是全局的;
如果全局下没有这个变量:1、设置
:给全局对象window增加了属性名;2、获取
:报错
查找私有变量
js中有且只有两种私有变量
:
1. 在私有作用域变量提升阶段声明过的变量或者函数
2. 形参也是私有变量
1 | function fn(num1,num2){ |
函数执行的步骤
函数执行形成一个新的私有作用域 ,在私有作用域内:
- 形参赋值(附带变量声明与赋值)
- 变量提升
- 代码自上而下执行
- 当前栈内存(私有作用域) 销毁或者不销毁
1 | //=>1.全局下的变量提升:var x;var y; var z; fn=aaafff111; |
1 | function fn(b){ |
1 | function fn(b){ |
如何查找上级作用域:看是在哪定义的
函数执行形成一个私有的作用域(A),A的上级作用域是谁
和他在哪执行的没关系,主要看他是在哪定义的
,在哪个作用域下定义的,当前A的上级作用域就是谁;
1 | var n=10; |
1 | //=>window 全局作用域 |
{}只是堆内存块级作用域, 不是作用域(作用域必须是函数执行形成) 所以自执行函数的上级作用域是 全局window
1 | //=>window 全局作用域 |
1 | var n=10; //=>n=10 |
闭包(自执行函数)
函数执行,
形成
一个私有作用域
,保护里面的私有变量不受外界的干扰
,这种保护机制就叫**闭包
**但是现在也有很多人认为,函数执行,形成一个不销毁的私有作用域,除了保护私有变量以外,还可以存储一些内容,这样的模式才叫闭包
1 | var utils=(function(){ |
闭包作用(保护
):函数执行形成私有作用域,保护里边的私有变量不受外界干扰
真实项目中,我们利用这种机制,实现团队协作开发(避免了多人同一命名,导致代码冲突的问题)
团队协作开发,每个开发者把自己的代码存放在一个私有的闭包当中,防止相互冲突;把需要供别人使用的方法,
通过return 或者 window.XXX暴露在全局下即可
;
1 | //=>A |
jQuery源码中也是利用保护机制实现的
1 | ~function(){ |
闭包作用(保存
):形成了不销毁的私有作用域(栈内存),保存里边的内容
函数执行形成一个私有作用域,函数执行完成之后,形成的这个栈内存一般情况下都会自动释放
但是还有特殊情况:
函数执行完成,当前私有作用域(栈内存)中的某一部分内容被栈内存以外的其他东西(变量/元素的事件)占用了,当前的栈内存就不能释放掉,也就形成了不销毁的私有作用域
(里边的私有变量也不会销毁)(返回了引用数据类型的数据 并被外界引用
)函数执行形成一个私有作用域,如果私有作用域中的部分内容被以外的变量占用了,当前作用域不销毁
形式
函数执行反回了一个引用数据类型堆内存地址
(并且堆内存属于这个作用域),在外面有一个变量接受了这个返回值,此时当前作用域就不能销毁(想要销毁,只需要让外面的变量赋值为null,也就不占用当前作用域中的内容了)
1 | function fn(){ |
函数执行后
return
一个对象或者函数(引用数据类型
) 返回了一个地址
,并被外面占用
(赋值给了外面的变量)那么这个函数执行形成的栈内存就不销毁了,如果return的是一个基础数据类型则销毁
fn每次执行
是重新
把所有流程再走一遍
(形参赋值 变量提升
),和别的fn执行无关
选项卡闭包解决办法
1 | //选项卡 |
obj={} {}只是堆内存 不是作用域、
window既不是关键字也不是保留字
i++和++i的区别
:
i++
:先拿原有i的值和其他值进行运算,运算完成后再自身累加,加括号也是先运算再累加
++i
:先自身累加1,然后拿累加完的值和其他值进行运算
1 | var i=5; |
1 | var i=5; |