JavaScript闭包

忍者秘籍

closure是一个函数在创建时允许该自身函数访问并操作自身函数之外的变量时所创建的作用域
闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域

你不知道的Javascript

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用
域之外执行。

作用域闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function foo() { 
var a = 2;

function bar() {
console.log( a );
}

return bar;
}

var baz = foo();

baz(); // 2 —— 朋友,这就是闭包的效果。

function foo() {
var a = 2;

function baz() {
console.log( a ); // 2
}

bar( baz );
}

function bar(fn) {
fn(); // 快来看呀,这就是闭包!
}

函数 bar() 的词法作用域能够访问 foo() 的内部作用域。然后我们将 bar() 函数本身当作一个值类型进行传递。在这个例子中,我们将 bar 所引用的函数对象本身当作返回值。

在定时器、事件监听器、Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!

  • 具名函数(命名函数)
  • 匿名函数(anonymous)
  • JS闭包面试题

    1、声明函数

1
2
最普通最标准的声明函数方法,包括函数名及函数体
function fn(){}

2、创建匿名函数表达式

1
2
3
4
5
6
7
创建一个变量,这个变量的内容为一个函数
var fn=function (){}

意采用这种方法创建的函数为匿名函数,即没有函数name

var fn=function (){};
getFunctionName(fn).length;//0 回头看下忍者秘籍

3、创建具名函数表达式(内联函数)

1
2
3
4
5
6
7
8
9
创建一个变量,内容为一个带有名称的函数

var fn=function hasName(){};

注意:具名函数表达式的函数名只能在创建函数内部使用

即采用此种方法创建的函数在函数外层只能使用fn不能使用hasName的函数名。hasName的命名只能在创建的函数内部使用

注意:在对象内定义函数如var o={ fn : function (){…} },也属于函数表达式

4、Function构造函数

1
2
3
4
5
6
7
8
9
10
可以给 Function 构造函数传一个函数字符串,返回包含这个字符串命令的函数,此种方法创建的是匿名函数。

Function('console.log(123)')
// ouput
ƒ anonymous(
) {
console.log(123)
}
Function('console.log(123)')()
// 123

5、自执行函数

1
2
3
4
(function(){console.log(1);})();

(function fn(){console.log(1);})();
自执行函数属于上述的“函数表达式”,规则相同

6、其他创建函数的方法

当然还有其他创建函数或执行函数的方法,这里不再多说,比如采用 evalsetTimeoutsetInterval等非标准方法

使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的不能访问到

1
2
3
4
5
6
7
8
9
10
11
12
var o={
fn:function (){
console.log(fn);
}
};
o.fn();//ERROR报错

var fn=function (){
console.log(fn);
};
fn();
// function (){console.log(fn)}