The Little JavaScript Closures

寫在前面

本文嘗試模仿 The Little Schema 的風格,介紹 JavaScript 的閉包。本文同時也是我學習 JavaScript 閉包的一次總結。歡迎一塊兒討論。javascript

簡介

什麼是閉包?html

閉包是一個函數java

閉包都是函數嗎?git

github

函數都是閉包嗎?web

編程

我怎麼判斷一個函數是否是閉包?閉包

你如今還不能回答,由於你還不知道如下概念:
全局變量(Global Variable)
局部變量(Local Variable)
自由變量(Free Variable)
詞法做用域(Lexical Scope)wordpress

變量與做用域

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 函數是一個閉包嗎?

是的,由於它是一個內部函數,同時引用了自由變量

閉包有什麼特色?

  1. 閉包能夠訪問外部變量

  2. 閉包能夠在外部函數返回以後依然保留外部變量的引用

  3. 閉包會保留外部變量的引用,不是該變量的值

第一點在前面的例子中已經懂了。

很好

第二點還沒懂,能夠舉個例子嗎?

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...

相關文章
相關標籤/搜索