sunmaobin / sunmaobin.github.io

blog
https://github.com/sunmaobin/sunmaobin.github.io
174 stars 11 forks source link

JS解惑-闭包(closure) #16

Open sunmaobin opened 7 years ago

sunmaobin commented 7 years ago

对于JS的闭包(Closure)的概念,以及闭包的作用域具有全局性。我针对我自己的实战感受,这里简单谈谈。

概念

闭包(Closure),是指有权访问另一个函数作用域中的变量的函数。

创建闭包,常见的一种方式就是在一个函数内部创建另一个函数。

概念引自:JavaScript 高级程序设计(第三版)7.2 闭包 一节,178页

示例

举个例子来解释下这个概念:

var name = 'window';
function outerFun(){
    var name = 'night';
    console.log('1',this.name);
    var innerFun = function(){
        console.log('2',name);
        console.log('3',this.name);
    };
    return innerFun;
};

outerFun()();

结果:

1 window
2 night
3 window

分析

示例代码在执行的时候,可以进行如下分解:

var name = 'window';
var fun1 = outerFun();
var fun2 = fun1();//fun2是fun1的闭包
fun2();//最终调用

再来继续加工下:

window.name = 'window';
window.fun1 = window.outerFun();
window.fun2 = window.fun1();//fun2是fun1的闭包
window.fun2();//最终调用

为什么能这么变形呢?因为在最外层,其实所有变量都是window对象的!

经过上面的分解,我们知道最终调用 fun2() 的时候,其实fun2函数,已经相当于是window环境了。

所以,我们知道任何闭包,都是最终都相当于在window环境下执行的,这也就是为什么闭包的作用域具有全局性了。

深究

接下来再来看单独拆分下每个函数:

fun1的函数:

var name = 'window';
function fun1(){
    var name = 'night';
    console.log('1',this.name);
}

为什么 fun1 中的结果是:window 而不是 night?

知识点:

  1. 我们知道,任何一个函数在执行的时候,首先会为其创建一个上下文环境,接着初始化2个变量:thisarguments,fun1中的this(调用它的对象,也叫活动对象),即:window
  2. 全局作用域局部作用域 的最大区别就是,局部作用域在函数内部定义,并且在非闭包的情况下,函数运行完毕就释放掉了。

所以:

console.log('1',this.name);其实就是在window中寻找 name 属性,这个肯定是 window 了,因为在函数之前,我们定了 var name= window.name

至于 fun1 中的 name 其实在函数中,根本就没有被使用,而如果我们不明确指定 this.name,即:

var name = 'window';
function fun1(){
    var name = 'night';
    console.log('1',name);
}

那么结果肯定是:night,再如果fun1内部未定义name,那么最终还是会通过原型链(或者叫上下文)找到全局的name。

fun2的函数:

function fun2(){
   console.log('2',name);
   console.log('3',this.name);
}

fun2所处的活动对象其实有3个:

  1. 自身函数
  2. fun1函数
  3. window函数

console.log('2',name); 会优先从内部寻找 name 变量,没找到!那么就从为其提供闭包的父函数 fun1 中找,找到了,即:night

console.log('3',this.name); 这里明确指定了 this.name 那么,系统就会从原型链开始寻找 this.name ,由于 this 指向的是 window ,那么结果自然就是:window

扩展

其实闭包函数,在我们的日常工作中经常用到。

定义对象过程中经常会涉及到

//通过字面量定义一个对象obj
var obj = {
    name : 'night',
    bindEvent : function(){
        var _self = this;
        $('#btn').click(function(){
            console.log('hello ' + _self.name);
        });
    }
};

方法中包含setTimeout

//3s后显示:hello night
function delayDisplay(){
    var name = 'night';
    setTimeout(function(){
        console.log('hello ' + name);
    },3000);
};

其实,还有很多这方面的例子,但是不管怎么样,你要记住一个概念:函数中的函数。

而且闭包有一个优势:就是能访问创建闭包对象的变量。

haoxunba commented 7 years ago

fun2的this不应该指向的是fun1吗?怎么this直接指向最外面的window

sunmaobin commented 7 years ago

@haoxunba 这说明你还没完全理解闭包,要理解this到底指向谁,主要是看是谁执行的当前函数,那么函数中this就指向谁。这个题目中,fun1的目的是return fun2函数,但是真正执行fun2的是window。