JS闭包

作用域(scope)的概念

JS中作用域就是栈内存,包括三种:

  • 全局作用域:window
  • 私有作用域:函数执行
  • 块级作用域:使用let创建变量存在块级作用域

作用域链

函数执行形成一个私有作用域(保护私有变量),进入到私有作用域中,首先变量提升(声明过的变量是私有的),接下来执行代码:

  1. 执行的时候遇到一个变量,若为私有变量,则按私有处理即可。
  2. 若当前这个变量不是私有的,则需要向它的上级作用域查找,上级作用域如果也没有,则继续向上查找,一直找到window全局作用域为止,我们把这种向上一级查找的机制叫做作用域链
    (1)若上级作用域有这个变量,我们当前操作的都是上级作用域中的变量(加入我们在当前作用域把值改了,相当于把上级作用域中的这个值给修改了)。
    (2)若上级作用域中也没有这个变量(一直找到window也没有):
    【设置】 变量 = 值(例如a = 12;):相当于给window设置了一个属性a,以后再操作window下就有了。
    【获取】alert(变量):想输出该变量,此时找不到,所以报错。

注意下面两种赋值的区别:

var x = 10, y = 20; //相当于var x = 10; var y = 20;所以有两个变量提升

var a = b = 100; //相当于var a = 100; b = 100; 只对a进行变量提升

私有变量

JS中的私有变量只有两种:

  1. 在私有作用域变量提升阶段,声明过的变量(或函数)。
  2. 形参也是私有变量。
function fn(n1, n2){
    var total = n1 + n2;
    return total;
}
var res = fn(100, 200);
console.log(res);

函数执行形成一个新的私有作用域:

  1. 形参赋值
  2. 变量提升
  3. 代码自上而下执行
  4. 当前栈内存(私有作用域)销毁或不销毁
// 变量提升:var x; var y; var z; f=function
var x = 10, y = 20, z = 30;
// 赋值: x=10; y=20; z=30;
function f(x, y) {
    //形参赋值:x=10; y=20;
    //变量提升:var x;(忽略,已经存在x了)
    console.log(x,y,z); //=>10 20 30
    var x = 100;    // 形参x=100
    y = 200;    // 形参y=200;
    z = 300;    //全局作用域中的z=300
    console.log(x,y,z); //=>100 200 300
}
f(x,y,z);
console.log(x,y,z); //=>10 20 300

function fn(b) {
    // 形参赋值:b=1;
    // 变量提升:b = function
    console.log(b); //=>[Function: b]
    function b() {
        // 没有形参赋值和变量提升
        // 所以这里b得从上级作用域中查找
        console.log(b); //=>[Function: b]
    }
    b();
}
fn(1);

如何查找上级作用域

先看一个栗子:

var n = 10;
function sum() {
    console.log(n);
}
sum();  //=>10
~function () {
    var n = 100;
    sum();  //=>10
    //此时sum的宿主环境是当前自执行函数形成的私有作用域
    //但是sum的上级作用域还是定义sum的地方,即全局作用域
}();

函数执行形成一个私有作用域(A),A的上级作用域是谁,和它在哪执行的没关系,主要是看它在哪定义的,在哪个作用域下定义的,当前A的上级作用域就是谁

var n = 10;
var obj = { //是个堆内存,不是作用域
    n: 20,
    fn: (function () {  //上级作用域:window
        var n = 30;	//若注释掉这行代码,输出10
        return function () {    //上级作用于:自执行函数
            console.log(n);
        }
    })()
};
obj.fn();   //=>30

将上面的代码改一下:

var n = 20;
var obj = { //是个堆内存,不是作用域
    n: 20,
    fn: (function (n) {  //上级作用域:window
        return function () {    //上级作用于:自执行函数
            console.log(n);
        }
    })(obj.n)   //这里报错,TypeError: Cannot read property 'n' of undefined
};
obj.fn();  

在这里插入图片描述

闭包的作用

1. 保护

形成私有作用域,保护里面的私有变量不受外界干扰。
在真是项目中,我们利用这种保护机制实现团队协作开发(避免了多人同一个命名导致的代码冲突问题)。

2. 保存

函数执行形成一个私有作用域,函数执行完成,形成的这个栈内存一般情况下都会自动释放。
但是还有二般情况:函数执行完成,当前私有作用域(栈内存)中的某一部分内容被栈内存意外的其他东西(变量/元素的事件)占用了,当前的栈内存就不能释放掉,也就形成了不被销毁的私有作用域(里面的私有变量也不会销毁)。
举个栗子:

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

注:函数执行形成一个私有作用域,如果私有作用域中的部分内容被外面的变量占用了,当前作用域不被销毁,通过以下形式实现【保存】

  • 函数执行返回了一个引用类型数据(堆内存)的地址(并且堆内存隶属于这个作用域),在外面有一个变量接收了这个返回值,此时当前作用域就不能被销毁(想要销毁,只需让外面的变量赋值为null,也就是不占用当前作用域中的内容了)。

综合练习:

// 变量提升:var num; var obj; var fn;
var num=1,  // num = 1;
    obj={
        num: 2,
        fn: (function (num) {
            // 形参赋值:num=1;
            // 变量提升:没有
            this.num *= 2;  // 自执行函数的this为window, 所以window.num=2;
            num += 2;   // 形参num=3;
            return function () {
                this.num = num;
                num++;
                console.log(num);
            }
        })(num) //实参num=1;是全局作用域中的num
    };
var fn = obj.fn;
fn();   //=>4
/*执行fn,此时
  fn = function () {
    // 形参赋值和变量提升都没有
    this.num = num; // fn前面没有点,所以this是window,window.num=num,
                    // 当前作用域没有num,从上级作用域(自执行函数的作用域)找num,num=3
                    // 所以window.num=3;
    num++;  // 还是上级作用域的num,num=4;
    console.log(num);   //=>4
}*/
obj.fn();   //=>5
/*obj.fn = function () {
    this.num = num; // this-->obj,所以obj.num=num,
                    // 此时上级作用域的num=4
                    // 所以obj.num=4;
    num++;  // 还是上级作用域的num,num=5;
    console.log(num);   //=>5
}*/
console.log(num, obj.num);  //=>3 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值