在Linux或MacOS系統中,ln
命令是一個重要的命令,它的功能是爲某一個文件在另一個位置創建一個同步的連接。php
對於前端來講,ln 命令被應用最多的地方就是, 就是全局安裝並建立一個 npm 命令html
npm i -g xxx(nrm)
當敲下回車,上面的安裝執行完成後,在輸出中,會看到這樣一串字符:前端
/usr/local/bin/nrm -> /usr/local/lib/node_modules/nrm/cli.js
這串字符背後的意思就是系統創建了node_modules/nrm/cli.js
的軟連接/bin/nrm
node
其實bin文件夾中的可執行命令,基本都是以軟連接的形式存在。linux
更多關於 ln
的使用,可參考菜鳥教程git
下面會圍繞路徑A 和 B 這兩個實例來說軟硬連接和文件拷貝的區別: github
路徑A: /user/wam/A/request.js npm
路徑B: /user/wam/B/request.js編輯器
request.js 內容函數
import utils from './utils'; console.log('res:', utils.res());
在開始前,簡單回顧一下大學沒學過,可能在那裏看到過的文件系統,這裏圍繞簡單易理解的Linux爲例。
大部分的Linux文件系統(如ext二、ext3)規定,一個文件由目錄、節點(inode)和數據塊(block)組成
當咱們隨便打開咱們某個開發項目,命令行輸入ls -li
, 就可看到目錄與inode
的對應信息,下圖第一行就是目錄對應的inode。
因爲一個文件塊(block)的大小有限(一般爲4kb),全部經常一個文件須要存儲在多個文件塊中,這樣 inode 就須要存儲多個block的位置信息(如最上圖所示), 而一個inode自己只有128 Btyes 的存儲空間,因此存儲文件block位置也是間接經過block來作的, 因此block 能夠理解爲分兩種: 文件內容block 與 inode信息block,搞懂這些就能夠往下了。
參考資料
軟連接(soft link) 又被稱爲符號連接,至關於Window 系統中的快捷方式。
eg: 創建A 爲 B 的軟連接
ln -s /user/wam/B/request.js /user/wam/A/request.js
創建軟連接 其實質就是某爲路徑的創建一個超連接
(在這表現爲 A 爲 B的超連接),其不具備文件實體。當咱們嘗試打開A 路徑所在的文件,其最終在編輯器打開的是路徑B的文件,因此其文件內的相對路徑引用文件也是相對路徑B來計算的,即utils 文件路徑爲:
/user/wam/B/utils.js
當刪除B文件,再去訪問A, 會發現索引不存在,沒法訪問。
硬連接(hard link), 是爲源文件創建另外一個索引。
eg: 創建A 爲 B 的硬連接
// 少一個 -s 選項 ln /user/wam/B/request.js /user/wam/A/request.js
創建硬連接 其實質就是爲文件實體建立另外一個可訪問的路徑索引
。因此當咱們嘗試打開A 路徑所在的文件,與軟連接區別的是:其最終在編輯器打開的是路徑A本身,因此其文件內的 相對路徑引用文件 也是相對路徑A
來計算的, 即utils 文件路徑爲:
/user/wam/A/utils.js
但值得一提的是,因爲 A 與 B 路徑都指向同一個源文件,因此在A路徑對文件內容所作的編輯都會反映在 B 路徑文件,即兩邊文件的變更是相互同步影響的。
當刪除路徑B時,源文件不會被垃圾回收,由於路徑A 仍保持對源文件的索引。
硬連接和軟連接還有一個區別是:由於系統的限制,硬連接要求路徑是在文件維度,而軟連接既能夠是文件,也能夠是文件夾。
這個應該用過電腦的人都懂。
eg: 拷貝文件B 到路徑 A
// 少一個 -s 選項 cp -f /user/wam/B/request.js /user/wam/A/request.js
文件拷貝,是平常咱們最多見的操做,只是更常見的形式是用ctrl + c/v
,而非cp
命令(實際上cp 也能實現ln
連接的操做), 其實質就是拷貝一份文件實體並建立一個可訪問的路徑索引
。因此當咱們嘗試打開A 路徑所在的文件,其指向的實體是不一樣於B的(克隆體),因此其文件內的相對路徑引用文件也是相對路徑A來計算的即utils 文件路徑爲,與創建硬連接一致:
/user/wam/A/utils.js
因爲 A 路徑 與 B 路徑 都分別指向本身的實體,因此A/B各自是獨立的,當刪除B時,B對應的源文件會被回收,A不受任何影響。
做爲前端咱們都知道,JS對象(object)是引用類型。
引用類型的值是保存在內存中的對象。JS 不容許直接訪問內存中的位置,即不能直接操做對象的內存空間。在操做對象時,其實是在操做對象的引用而不是實際的對象。爲此,引用類型的值是按引用訪問的。(摘抄自紅寶書 P87)
你品,你細品。是否是感受 引用類型 和咱們上面講到文件連接與源文件很像。
以:
const B = { a: 1 };
因此當執行下面這種操做:
const C = B; C.a = 2; console.log('B.a:', B.a); // 2 B.a = 3; console.log('C.a:', C.a); // 3
從上面的執行輸出,咱們能夠很容易看出,原來JS中的引用類型變量賦值和硬連接
是一回事。
接着咱們引入一個lodash 的深拷貝(cloneDeep)函數:
import { cloneDeep } from 'lodash'; const C = cloneDeep(B); C.a = 2; console.log('B.a:', B.a); // B.a: 1 B.a = 3; console.log('C.a:', C.a); // C.a: 2
從上面的執行輸出,咱們能夠很容易看出,原來JS中的深拷貝和文件拷貝
是一回事。
哪軟連接
怎麼用 JS 來描述呢?Proxy?
Proxy 中文譯做代理
,在表現上實際上是與硬連接一致的,而硬連接與軟連接從表現上最大的區別就是:B(母體) 被刪除後,A(超連接)就不可訪問了,因此這並非正確的答案。
而正確答案是:WeakRef
,弱引用. 當下處於proposal階段,不過在Chrome 與 Firefox 最新的版本都對其作了實現;
看個demo:
let B = { a: 1 }; const C = new WeakRef(B); const registry = new FinalizationRegistry(heldValue => { console.log('GC worked:', heldValue); // GC worked: B // 當B所指向的值被垃圾回收後,這個回調將被執行 console.log('C.a:', C.deref()?.a); //C.a: undefined }); // 註冊B所指向的值被垃圾回收的監聽 registry.register(B, "B"); console.log('C.a:', C.deref().a); // C.a: 1 C.deref().a = 2; // 經過索引改變值 console.log('B.a:', B.a); // B.a: 2 B.a = 3; console.log('C.a:', C.deref().a); // C.a: 3 // 切斷對值得索引, 觀察上面的GC 回調 B = null; // console.log('after C.a:', C.deref()?.a);
貌似上面的JS代碼能勉強闡述軟連接的原理,但離理想確實還有距離,這個神侃更多的是想讓你們對ES新提案中的WeakRef
與 FinalizationRegistry
有一個感性的認識。
更多關於 WeakRef
請閱讀
經過本文,你是否是發現,這世間萬事萬物是否是特別奇妙。雖然是神侃,讀到這裏,但願你能有一絲絲收穫。
原文見: https://github.com/closertb/c...