2014
Mar
21

Javascript 是一个很特别的程式语言,与传统的 C 语言有很大的落差,这篇文章会介绍两个 Javascript 独有的特性 Hoisting 与 Closure , Closure 也有人翻译成闭包

Closure

Closure 很特别,目前在各种程式语言中,很少会支援这个特性的,一般来说变数的生命周期,只会生存在该 function ,一旦离开了 function ,变数便会被回收,不能够再被使用,而且每个在 function 中的变数,一定要事先宣告,在 function 中的变数,我们称之为 Local Variable,function 只能使用自身的 Local Variable ,不能使用 function 外的变数,但是在 Javascript 中却没有这么简单,来看一个 function 使用外部变数的范例。

closure1.js
  1. var a = 1;
  2.  
  3. function closure1() {
  4. console.log(a)
  5. }
  6. closure1();

上一段程式会印出 「1 」,然而在 function closure1 中没有宣告 a 这个变数,但是 closure 的 parent 中宣告了 a 这个变数,所以 Javascript 会自动去取得 a =1 。

再来看一个使用昵名 function 的范例。

closure2.js
  1. function closure2() {
  2. var a = 2;
  3. var s = function () {
  4. console.log(a);
  5. }
  6. s();
  7.  
  8. }
  9.  
  10. closure2();

这段程式会印出 「2」,原理跟第一个范例相同, closure2 中执行了 s() ,所以 s() 的 parent 是 closure2 ,根据 closure 的特性 s() 可以使用 closure2 的变数。

再来看一个奇怪的范例,你应该知道输出的结果是什么了吧。

closure3
  1. var a = 1;
  2. function closure3() {
  3. var s = function () {
  4. console.log(a);
  5. }
  6. s();
  7. }
  8. closure3();
Answer = 1 , s() -> parent (closure3) -> parent (global)

使用 Closure 时要特别小心,不好的 Closure 很容易造成 memory leak ,其实笔者我平常是不使用 Closure 这种写法的。

Hoisting

Hoisting 跟变数的宣告有关系,一个 JS function 可以重覆定义相同的变数名称,如果在 C 语言你重覆定义相同的变数的话,你连 Compile 都会过不了,但是这在 Javascript 中是完全合法的。

Hoisting1
  1. var a = 1;
  2. function hoisting1() {
  3. if (!a) {
  4. var a = 2;
  5. }
  6. console.log(a);
  7.  
  8. }
  9.  
  10. hoisting1();

上一段程式,会印出 「2 」这个值,疑! 刚刚才说过 closure 的观念, a 不是应该就自动去取得他的 parent 的值吗 (a=1)。

Javascript 有另一个特性 Hoisting ,程式会将 function 中全部需要宣告的 Local Variable ,提升到 function 的第一行来执行。

来看看 Javascript 真正的执行过程:

Hoisting1 in javascript engine
  1. var a = 1;
  2. function hoisting1() {
  3. var a;
  4. if (!a) {
  5. a = 2;
  6.  
  7. }
  8. console.log(a);
  9. }
  10.  
  11. hoisting1();

看完真正 JS Engine 的执行码,就可以很清楚的知道原因, Local Variable a 在 function 的第一行就已经被宣告了,这时 a 会等於「undefined」,所以才造成程式进入 if (!a) 的区块。



回應 (Leave a comment)