Beginner Level Question for Javascript Closure

陳奕熹
陳奕熹
Mar 11 · 5 min read

最近在看 ROS 2 程式範例的時候看到它用到了 Lambda 的寫法,印象中只有在一場 COSCUP 的議程聽到 functional language 聽到有這樣的寫法(雖然聽了半天也不是很懂在做啥)

上網查了下發現是很多高階語言都會支援的寫法,但

“C is a notable exception” by What is a closure?

最近都在寫 C 的東西,之前寫網頁 javascript 也沒有用到太難的東西,就索性當 C 來寫,於是雖然閉包 (Closure) 這樣的觀念已經早就不是新聞了,但是對我來說還真的是沒用過


相關對於閉包的說明網路上已經很多了,根據我在 openhome.cc 的閉包說明中大概的理解就是

“閉包就是延長變數生命週期的方法”

這個改念真的蠻神奇的,至少在 C 語言規格書已經很詳細的定義所有變數的生命週期了,在宣告變數的同時基本上就不能再更改它的生命週期了

該文章透過這個例子示範閉包的使用方法

function doOther() {
var x = 10;
function f(y) {
return x + y;
}
x = 100;
return f;
}

var foo = doOther();

foo(20);

根據說明, f(y) 會去拿取變數 x,並且外部環境對於 x 記憶體的變更都會影響 f(y) 的輸入

不過看到這個程式,讓我不禁想到了幾個問題跟假設

  1. Javascript 可以回傳 function variable ? 而且這個 function variable的值等同於 function return value ?
    C 語言的確也有 function pointer 這種東西,但是多用在多型或是將實做跟界面區別開來的手段,所以在使用他的時候還是作為 function call 使用
    把 function variable 視為一個數值,並對它做加減運算這種事真沒想過
  2. 這樣子的實做是 multi-thread 嗎 ?
    因為上述說明提到 「closure 會抓到外部變數的變化」,這件事讓我不禁開始想,「那是什麼時候開始抓」,並且「closure 什麼時候回傳呢」
    第一個想法是多執行緒,當 doOther() 執行到 f(y) 時直接開一條新的 thread 給它用,也因為這樣 doOther() 的變數記憶體發生變化,因為 thread 的記憶體是共用的所以 f(y) 也可以拿到這樣的變化
    但是這樣就遇到一個很大的問題了,萬一原始程式改成這樣
function doOther() {
var x = 10;
function f(y) {
return x + y;
}
heavy_jobs(10); /* 假設代表繁忙的運算工作 */
x = 100;
return f;
}

var foo = doOther();

foo(20);

在 doOther() 對 x 值兩次的更改間插入一個龐大的運算,這樣運算所需要花費的時間會隨著硬體處理器的效能而有所變化,這樣我怎麼能夠確認 f(y) 在進入時拿到的 x 是不是我要的呢 ?有可能這次處理器比較好所以趕得及在 f(y) 執行時算出確定的 x ,但是也有可能今天電腦比較爛拿不到想要的 x

這個概念也有點像是計算機組織提到不同邏輯晶片處理時間不同,像是在一個 clock 時脈週期中必須要等到後半週期才能給出運算後正確的電壓值,其他時候都是無意義的電壓訊號,如果不能夠確認這個「正確訊號出現的 deadline」,那這個晶片其實就沒啥用了

那第二種情況,它有沒有可能是等 doOther() 先做完才執行 f(y) ? 這樣就可以解決上述多工資料問題,類似 Makefile 預設會讀到變數改變後最終值一樣?


為了驗證以上奇葩的想法,我假設「 f(y) 可以視為一個正常的數值,像 int 一樣接受加減乘除」,並且「doOther() 先做完才做 f(y)」

所以我做了以下的實驗

function closure_test() {
var x = 10;
function ins_x(increment) {
return x + increment;
}
x = 20;
x += ins_x;
return x;
}
var foo = closure_test();foo(20);

很可惜的是,以上是編不過的,我猜測這是因為不能夠回傳區域變數?
因為以下這樣還是不能動的

function closure_test() {
var x = 10;
function ins_x(increment) {
return x + increment;
}
x = 20;
return x;
}
var foo = closure_test();foo(20);

考慮到我不熟悉 javascript 編譯器的實做,我嘗試用另外一種方式寫

function closure_test() {
var x = 10;
function ins_x(increment) {
return x + increment;
}
x = 20;
ins_x += x; /* 前後順序互換 */
return ins_x;
}
var foo = closure_test();foo(20);

在 C 語言編譯器很顯然是沒有用的,因為兩者會轉成同樣的賦值形式

而很顯然的,javascript 也無法通過,於是我索性做最後一次智障的嘗試

function closure_test() {
var x = 10;
function ins_x(increment) {
return x + increment;
}
x = 20;
ins_x += x; /* 前後順序互換 */
return 20;
}
var foo = closure_test();foo(20);

看來是因為 ins_x 本身就無法與 x 運算,這才產生出來的 syntax error


透過以上的實驗,我覺得去看 javascript 規格書會是一個好辦法,只不過耳聞 javascript 是一個語法很混亂的語言…… 怕就怕它其實沒有針對這件事做詳細的規範定義

陳奕熹

Written by

陳奕熹

應該要是一個工程師的……

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade