[TOC]
## previously
函数执行形成一个私有作用域,保护里面的私有变量不受外界干扰就叫闭包
### 作用域
[栈内存]
> 全局作用域:window
> 私有作用域:函数执行
> 块级作用域:使用let创建变量,存在块级作用域中
### 作用域链
当前作用域代码执行的时候遇到一个变量,我们首先看一下它是否属于私有变量,如果是当前作用域私有变量,那么以后在私有作用域中再遇到这个变量都是操作私有的(“闭包:私有作用域保护私有变量不受外界干扰”);如果不是私有的变量,向其上级作用域查找,如果也不是上级作用域私有的,就会继续向上查找,直到找到window全局作用域,我们把这种向上一级级查找的机制叫做`作用域链`
全局下有,操作的就是全局变量,全局下没有(设置:给全局对象window增加了属性名&&获取:报错)
## 查找私有变量
>JS中的私有变量有且只有两种
>- 在私有作用域变量提升阶段声明过的变量或则函数
>- **形参**也是私有变量
```
function fn(num1,num2){
var total = num1+num2;
return total;
}
var result = fn(100,200);
```
>函数执行形成一个全新独立的私有的作用域
>1、 形参赋值
>2、 变量提升
>3、 代码自上而下执行
>4、 当前栈内存(私有作用域)销毁或则不销毁
**注意:** 形参赋值在变量提升之前
```
var x=10
,y = 20
,z = 30;
function fn(x,y){
//=>[私有作用域]
//=>形参赋值:x=10 y=20 (x/y都是私有变量)
//=>变量提升:var x(忽略的,已经存在这个名字了)
console.log(x,y,z); //=>z不是私有变量,是全局变量
var x = 100; //=>私有的x=100
y = 200; //=>私有的y=200
z = 300; //=>全局的z=300
console.log(x,y,z); //=>100 200 300
}
fn(x,y,z); //=>FN执行传的的是实参(实参都是值,不是指要转换成值,这里传递的不是变量,而是变量的值传过去了)
console.log(x,y,z); //=>10 20 300
```
FN执行传的的是实参(实参都是值,不是指要转换成值(**这个值可能是地址**),这里传递的不是变量,而是变量的值传过去了)
```
//=>[私有作用域]
//=>形参赋值:b=1 (私有变量)
//=>变量提升:b=aaafff111(此处赋值操作替换了形参赋值的内容)
function fn(b){
console.log(b); //=>函数b本身
function b(){
//=>[私有作用域]
//=>形参赋值和变量提升都没有
console.log(b);
}
b(); //=>函数b本身
}
fn(1);
```
## 如何查找上级作用域
```
var n = 10;
function sum(){
console.log(n);
}
// sum(); //=>10
~function(){
var n = 100;
sum(); //=>10 //sum的宿主环境(和箭头函数的宿主环境定义貌似不一样)是当前自执行函数形成的私有作用域,宿主环境和上级作用域没有关系
}();
```
>函数执行形成一个私有作用域(A),A的上级作用域是谁,和它在哪执行没有关系,主要是看他在哪定义的,在哪个作用域下定义的,当前A的上级作用域就是谁。
```
var n = 10;
var obj = {
n:20
,fn:(function(){
var n = 30;
//->上级作用域:全局作用域
return function(){
//->上级作用域:自执行函数
console.log(n);
}
})()
};
obj.fn(); //30//return function(){console.log(n);}
```
以上栗子,在堆内存中存obj的时候,遇到fn,发现是一个函数自执行,于是开始函数自执行,开辟一个栈内存,形参赋值,变量提升,逐行执行,然后return function,因为是function,此时会另外再开辟一个堆内存,返回堆内存的地址,作为return 返回的值。
![](https://box.kancloud.cn/6365e868e9eaf1dd13377a63ce6aa60b_1723x367.png)
```
var n = 10;
var obj = {
n:20
,fn:(function(n){
//->上级作用域:全局作用域
return function(){
//->上级作用域:自执行函数
console.log(n);
}
})(obj.n)
};
obj.fn(); //会报错
```
## 闭包作用之保护
>函数执行形成的私有作用域会保护里面的私有变量不受外界干扰。
```
(function(window,undefined){
var jQuery = function(){
...
};
...
window.jQuery = window.$ = jquery;
})(window);
```
```
var Zepto = (function(){
var Zepto = function(){
...
};
...
return Zepto;
})();
Zepto();
```
在真实项目中,我们利用这种保护机制实现团队协作开发。(避免了多人同一个命名,导致代码冲突的问题)
## 闭包作用之保存
> 函数执行,形成一个私有作用域,函数执行完成,形成的这个栈内存一般情况下都会自动释放。
>
>但是还有二般情况,函数执行完成,当前私有作用域中的某一部分内容被栈内存以外的其它东西(变量/元素的事件)占用了。当前的栈内存就不能释放掉,也就形成了不销毁的私有作用域(里面的私有变量也不会销毁)。
```
function fn(){
var i = 1;
return function(n){
console.log(n+i++);
}
}
var f = fn();
f(10);//11
fn()(10);//11
f(20);//22
fn()(20);//21
```
![](https://box.kancloud.cn/771f39d5b8dfead624a78c60d3e78f98_1359x602.png)
>函数执行形成一个私有作用域,如果私有作用域中的的部分内容被外部的变量占用了,私有作用域不会销毁。
>[形式]
>函数执行返回了一个引用数据类型堆内存的地址(并且这个堆内存是隶属于这个作用域的),在外面有一个变量接收了这个返回值,此时当前作用域就不能销毁(想要销毁,只需要让外面的变量赋值为null,也就是不占用当前作用域中的内容了)
### 有return不一定销毁
私有作用域里没有东西被占用,就会销毁。
### 不销毁不一定要有return
```
//这种形式私有作用域也不会销毁
for(var i=0;i<oList.length;++i){
~function(i){
oList[i].onclick = function(){
changeTab(i);
}
//没有return 但被事件绑定占用
}(i)
}
```
另外要注意,不是你return一个堆内存就不会销毁(上面的例子并没有return),而是要看return的这个是否被占用,如果没有变量来接收,是直接会销毁的
### 闭包保存在循环中的应用
```
//这样达不到预期的效果
for (var i=0;i<oList.length;++i){
oList[i].onclick = function(){
changeTab(i); //=>不行的原因,给当前LI点击事件绑定方法的时候,绑定的方法并没有执行(点击的时候才执行),循环3此,分别给3个LI的点击事件绑定了方法,循环完成后i=3(全局的)。当点击的时候,形成一个私有作用域,用到了变量i,i不是私有的变量,向全局查找,此时全局的i已经是最后循环的3了
}
}
//--- --- ---
for(var i=0;i<oList.length;++i){
oList[i].onclick = (function(i){
return function(){
changeTab(i);
}
})(i)
}
//--- --- ---
//这种形式私有作用域也不会销毁
for(var i=0;i<oList.length;++i){
~function(i){
oList[i].onclick = function(){
changeTab(i);
}
}(i)
}
//--- --- ---
//块级作用域 每次循环都会形成一个单独的{}块级作用域,每个块级作用域之间没有冲突
for (let i=0;i<oList.length;++i){
oList[i].onclick = function(){
changeTab(i);
}
}
```
- 空白目录
- window
- location
- history
- DOM
- 什么是DOM
- JS盒子模型
- 13个核心属性
- DOM优化
- 回流与重绘
- 未整理
- 文档碎片
- DOM映射机制
- DOM库封装
- 事件
- 功能组件
- table
- 图片延迟加载
- 跑马灯
- 回到顶部
- 选项卡
- 鼠标跟随
- 放大镜
- 搜索
- 多级菜单
- 拖拽
- 瀑布流
- 数据类型的核心操作原理
- 变量提升
- 闭包(scope)
- this
- 练习题
- 各种数据类型下的常用方法
- JSON
- 数组
- object
- oop
- 单例模式
- 高级单例模式
- JS中常用的内置类
- 基于面向对象创建数据值
- 原型和原型链
- 可枚举和不可枚举
- Object.create
- 继承的六种方式
- ES6下一代js标准
- babel
- 箭头函数
- 对象
- es6勉强笔记
- 流程控制
- switch
- Ajax
- eval和()括号表达式
- 异常信息捕获
- 逻辑与和或以及前后自增
- JS中的异步编程思想
- 上云
- 优化技巧
- 跨域与JSONP
- 其它跨域相关问题
- console
- HTML、XHTML、XML
- jQuery
- zepto
- 方法重写和方法重载
- 移动端
- 响应式布局开发基础
- 项目一:创意简历