资讯专栏INFORMATION COLUMN

The Little JavaScript Closures

Heier / 2939人阅读

摘要:写在前面本文尝试模仿的风格,介绍的闭包。本文同时也是我学习闭包的一次总结。注根据这篇文章,事实上所有函数在创建的时候都会形成闭包。但这种闭包并没什么趣味,也没什么特别的用途,所以我们更关注的是由内部函数形成的闭包。

写在前面

本文尝试模仿 The Little Schema 的风格,介绍 JavaScript 的闭包。本文同时也是我学习 JavaScript 闭包的一次总结。欢迎一起讨论。

简介

什么是闭包?

闭包是一个函数

闭包都是函数吗?

函数都是闭包吗?

我怎么判断一个函数是不是闭包?

你现在还不能回答,因为你还不知道以下概念:
全局变量(Global Variable)
局部变量(Local Variable)
自由变量(Free Variable)
词法作用域(Lexical Scope)

变量与作用域

var a = 1; a 是什么变量?

全局变量

a = 1; a 是什么变量?

全局变量

function foo() {
  a = 1;
  var b = 1;
}

这里的 a,b 分别是什么变量?

a 是全局变量,b 是局部变量

为什么 a 在函数中定义还是全局变量?

因为 a 不是用 var 声明的

不用 var 声明的变量都是全局变量?

是的

var 声明的变量都是局部变量?

不是

为什么?

在全局作用域中声明的变量都是全局变量,即使这个变量是用 var 声明的

全局作用域是什么?

函数作用域以外的地方都是就是全局作用域

函数作用域又是什么?

函数内部

可以举个例子吗?

var foo = 1;
function bar() {
  var baz = 2;
}

foo 变量和 bar 函数都处于全局作用域中,baz 变量处于函数作用域中

function foo() {
  var bar = 1;
}

这段代码中有多少个作用域?

2 个,foo 函数所处的全局作用域和 bar 变量所处的函数作用域

function foo() {
  var bar = 1;
  function baz() {
    var test = 1;
  }
}

这段代码中有多少个作用域?

3 个,foo 函数所处的全局作用域,bar 所处的函数作用域,和 test 所处的函数作用域

上面的 bar 变量和 baz 函数处于同一个作用域吗?

是的,因为它们都在 foo 函数中

上面 test 变量和 bar,baz处于同一个作用域中吗?

不是,因为 test 变量在 baz 函数中

JavaScript 用函数来划分作用域吗?

是的

function foo() {
  var bar = 1;
}
console.log(bar);

会输出什么?

Uncaught ReferenceError: bar is not defined

为什么会报错呢?

因为外部作用域不能访问内部作用域

var foo = 1;
function bar() {
  console.log(foo);
}
bar();

会输出什么?

1

为什么不会报错?

因为内部作用域可以访问外部作用域

var x = 1;
function foo() {
  var x = 2;
  console.log(x);
}
foo();

会输出什么?

2

为什么不是输出 1 ?

因为局部变量的优先级比外部变量高

var x = 1;
function foo() {
  console.log(x);
  var x = 2;
  console.log(x);
}
foo();

会输出什么?

undefined
2

为什么会这么奇怪?

因为变量声明有变量提升(Variable Hoisting)的过程

变量提升是什么?

声明语句会在执行前被处理,在任何地方声明一个变量,相当于在顶部位置声明

可以举个例子吗?

bla = 0;
var bla;

// 相当于

var bla;
bla = 0;

这和之前的例子有什么关系?

函数内部声明的变量,都会先在函数的顶部声明。所以之前的例子就相当于

function foo() {
  var x;
  console.log(x);
  x = 1;
  console.log(x)
}

什么是词法作用域?

变量的作用域是由它在源代码中所处位置决定的(词法),并且嵌套的函数可以访问到其外层作用域中声明的变量。

这和上面说到的内部作用域可以访问外部作用域有什么区别吗?

没有

什么是自由变量?

在函数内部使用到,但既不是该函数的参数,也不是该函数的局部变量的变量。

可以举个例子吗?

var foo = 1;
function bar() {
  var baz = 2;
  console.log(foo + baz);
}

这里 bar 函数有三个变量:baz, console, foo
其中 baz 是局部变量, console 和 foo 都属于自由变量

为什么 console 和 foo 都是自由变量?

因为 console 和 foo 都在全局作用域中,在 bar 函数中是通过引用的方式来使用 console 和 foo 的

还需要了解其他概念吗?

不需要,现在已经可以深入了解闭包了

闭包

什么是闭包?

闭包是一个内部函数 [注1]

内部函数都是闭包吗?

不是,引用了自由变量的内部函数才是闭包

var x = 1;
function foo() {
  console.log(x + 1);
}

foo 函数是一个闭包吗?

不是,因为 foo 函数不是一个内部函数

function foo() {
  function bar() {
    var x = 1;
    return x + 1;
  }
}

bar 函数是一个闭包吗?

不是,因为它只是一个内部函数,并没有引用自由变量

function foo() {
  var x = 1;
  function bar() {
    return x + 1;
  }
}

bar 函数是一个闭包吗?

是的,因为它是一个内部函数,同时引用了自由变量

闭包有什么特点?

闭包可以访问外部变量

闭包可以在外部函数返回之后依然保留外部变量的引用

闭包会保留外部变量的引用,不是该变量的值

第一点在前面的例子中已经懂了。

很好

第二点还没懂,可以举个例子吗?

function add(x) {
  return function(y) {
    return x + y;
  }
}

var add5 = add(5);
console.log(add5(10)) // 15

即便 add 函数已经返回,add5 中依然可以访问 x

第三点还没懂,可以举个例子吗?

function user() {
  var id = 1;

  return {
    getId: function() { return id; },
    setId: function(newId) { id = newId }
  }
}

var foo = user();
foo.getId(); // 1
foo.setId(2);
foo.getId(); // 2

这里闭包中的 id 是一个引用,不是实际值

有点像私有方法?

是的,我们可以用闭包来实现私有方法

闭包还可以用来做什么?

闭包是函数式编程的骨架,掌握闭包之后你可以写出函数式 JavaScript 代码。

函数式编程是什么?

这不是本文的讨论范围,自己去学习吧。

One More Thing

注1] 根据 [Understanding JavaScript Closures 这篇文章,事实上所有函数在创建的时候都会形成闭包。但这种闭包并没什么趣味,也没什么特别的用途,所以我们更关注的是由内部函数形成的闭包。

出处

https://scarletsky.github.io/2015/12/02/...

参考资料

http://uternet.github.io/TLS/
http://www.ruanyifeng.com/blog/2009/08/l...
https://developer.mozilla.org/zh-CN/docs...
https://developer.mozilla.org/en-US/docs...
http://javascriptissexy.com/understand-j...
http://javascriptissexy.com/javascript-v...
http://stackoverflow.com/questions/12930...
https://javascriptweblog.wordpress.com/2...
http://www.moye.me/2014/12/29/closure_hi...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78258.html

相关文章

  • JavaScript之闭包相关

    摘要:函数对象可以通过这个作用域链相互关联起来,如此,函数体内部的变量都可以保存在函数的作用域内,这在计算机的文献中被称之为闭包。所以按照第二段所说的,严格意义上所有的函数都是闭包。 Like most modern programming languages, JavaScript uses lexical scoping. This means that functions are e...

    sewerganger 评论0 收藏0
  • 分享一个关于匿名函数和闭包的问答

    摘要:引用一个的提问个人觉得总结的比较好的两句话原文地址另外,附上中对闭包的讲解闭包中文对于闭包的简要概括原文原文地址匿名函数和闭包来自文章作者版权声明自由转载非商用非衍生保持署名创意共享许可证转载请注明出处 引用一个stackoverflow的提问 个人觉得总结的比较好的两句话: An anonymous function is just a function that has no na...

    Barrior 评论0 收藏0
  • 掌握Javascript面试:什么是闭包?

    摘要:我的意思是大多数称职的面试官会问你什么是闭包,并且在大多数时候你回答错误将失去这份工作。在闭包的范围内定义的任何公开方法都是特权的。使对象的数据私有化并不是闭包的唯一用途。 文章来源于:https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36 在J...

    jifei 评论0 收藏0
  • javascript闭包介绍

    摘要:下面这个例子就是闭包,函数能够访问到不在其代码块里的变量。然而事实恰恰相反,唯一的解释就是是一个闭包。性能问题执行一次,就会重新构造两个函数。正确的做法应该是参考资料深入理解闭包学习闭包阮一峰 概念 闭包(closure)是一个拥有任意变量以及绑定这些变量的环境(environment)的表达式(一般来说是就是function) A closure is an expression (...

    weij 评论0 收藏0
  • JavaScript 语法树与代码转化实践

    摘要:语法树与代码转化实践归纳于笔者的现代开发语法基础与实践技巧系列文章中。抽象语法树抽象语法树的作用在于牢牢抓住程序的脉络,从而方便编译过程的后续环节如代码生成对程序进行解读。 JavaScript 语法树与代码转化实践 归纳于笔者的现代 JavaScript 开发:语法基础与实践技巧系列文章中。本文引用的参考资料声明于 JavaScript 学习与实践资料索引中,特别需要声明是部分代码片...

    Gemini 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<