iT邦幫忙

0

nodejs fs.Dirent 疑問

  • 分享至 

  • xImage

我取得某dir的內容之後,我想透過sort把資料夾放在陣列前面
我看到他有Symbol(type) 這個屬性,但訪問他都是Undefine

var fs = require('fs');
var path = require('path');

fs.readdir('/dir', {withFileTypes: true}, (err, files) =>{
    files.sort(function compare(a, b) {
        if(a['Symbol(type)'] > b['Symbol(type)']) {
            return -1;
        }
        if(a['Symbol(type)'] < b['Symbol(type)']) {
            return 1;
        }
        return 0;
    })
    files.forEach(element => {
        console.log(element);
    });
})

Output

Dirent {name: "xx.sh", Symbol(type): 1}
Dirent {name: "testDIR", Symbol(type): 2}
Dirent {name: "xxx.py", Symbol(type): 1}
https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_class_fs_dirent

用Dirent提供的方法來確認他的type。
jokie7585 iT邦新手 5 級 ‧ 2020-04-16 12:19:51 檢舉
謝謝你的回答~我把疑問統一寫在下面的回答裡面了,有興趣可以看看哦>w<感謝大大
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

1
listennn08
iT邦高手 5 級 ‧ 2020-04-16 10:25:59
最佳解答

Dirent class

裡面的 Symbol 屬性有好幾種
其中一種 isFile 可以確認是不是檔案
isDirectory確認是不是資料夾
會回傳 undefined 的原因是因為沒有叫 Symbol(type) 的物件
所以要依是否為資料夾做排序

var fs = require('fs');
var path = require('path');

fs.readdir('/dir', {withFileTypes: true}, (err, files) => {
    files.sort(el => el['isDirectory']() ? -1 : 0);
    files.forEach(element => {
        console.log(element);
    });
})
看更多先前的回應...收起先前的回應...
jokie7585 iT邦新手 5 級 ‧ 2020-04-16 12:08:46 檢舉

感覺我對Symbol有很大的誤解(剛接觸js不久)
為什麼明明是Symbol的property,但卻不需要用Symbol.property呼叫?

jokie7585 iT邦新手 5 級 ‧ 2020-04-16 12:09:39 檢舉
jokie7585 iT邦新手 5 級 ‧ 2020-04-16 12:21:38 檢舉

然後我想知道為什麼console會這樣印~
希望大大賜教QQ

應該說那些 function 都屬於 dirent 裡的 function
只是那些 function 是透過 Symbol 這個函數建立
確保他是獨一無二的 function
我的理解是這樣 也可能有不是我說的這樣
為什麼不需要 Symbol.property 呼叫 因為他是物件中的函數

參考1
參考2
不過 console 為什會這樣印我就不清楚了
他後面的數字看起來是 symbol function return true 的個數

我試了一下,用Ojbect.keys也抓不到用Symbol做的key。我猜測他是讓你在console.log可以看到資訊來檢查問題,但是使用上你必須透過他提供的isXXX方法來操作。

數字看起來是:1:檔案、2:目錄 ...

參考:
https://github.com/nodejs/node/blob/v12.16.2/lib/internal/fs/utils.js

41行:

const kType = Symbol('type');

89行開始:

class Dirent {
  constructor(name, type) {
    this.name = name;
    this[kType] = type;
  }
  //後面就是方法定義
  ...
}

kType變數只在這個模組內部有效。

fillanofeng

數字看起來是:1:檔案、2:目錄 ...

費大我剛剛也是這麼認為 不過我看到這篇有3 XD
我又確認了一次也不是我說的那樣
看起來的確是 1.檔案, 2.目錄

原來寫法長這樣 難怪我剛剛怎麼寫都不一樣/images/emoticon/emoticon13.gif

這些數字是在c++原始碼裡面定義的(我猜是列舉),node的實做也只用到常數名稱,數字的值其實不重要。

參考第四點:
https://2ality.com/2016/01/private-data-classes.html

猜測用Symbol的目的是當作私有成員之類,但是因為還是可以用Reflect.ownKeys()取出key來取值...不是夠好的方法XD

jokie7585 iT邦新手 5 級 ‧ 2020-04-16 18:38:11 檢舉

兩位的解說真的是太詳細了QQ 小弟由衷的感謝Orz
感覺get到很多知識點
謝謝兩位!

jokie7585 iT邦新手 5 級 ‧ 2020-04-16 19:11:04 檢舉

我好像資質愚鈍...
我知道class語法是語法糖 但我還不能很好的理解
不知道上面kType的作法是不是等價於這樣...

// iterable protocol宣告法
function myIterable(type) {
    this.StorageArray = {
        storage1: {slot1:'1', slot2:'2'},
        storage2: {slot1:'1', slot2:'2'},
    };
    this.length = 2;
    this.kType = type;
}
// 實作prototype中的Symbol中的'iterator'
myIterable.prototype[Symbol.iterator] = function() {
    const instance = this;
    return {
        i:0,
        next() {
            if(this.i < instance.length) {
                return {value: Object.values(instance.StorageArray)[this.i++], done: false};
            }
            return {value: undefined, done: true};
        }
    };
};

// 實作 Symbol.kType檢查
myIterable.prototype[Symbol.type1_storage] = function() {
    const instance = this;
    return instance.kType === 'type1_storage';
};

let myIterableTest = new myIterable('type1_storage');

for (const value of myIterableTest) {
    console.log(value);
}

console.log(myIterableTest[Symbol.type1_storage]());
jokie7585 iT邦新手 5 級 ‧ 2020-04-16 19:12:03 檢舉

我會有這樣的疑問是因為,這些"well-known"的Symbol可以這樣做 -
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator

// Satisfies both the Iterator Protocol and Iterable
let myIterator = {
    [Symbol.iterator]: function() { return this; }
};

他是編譯器編譯的結果嗎?
然後他就會在prototype中建立Symbol.iterator(?
因為我用VSCode去看,我把游標移動到'Symbol'字樣上時
它顯示了'symbolConstructor的字樣'

jokie7585 iT邦新手 5 級 ‧ 2020-04-16 20:45:58 檢舉

目前上面的程式碼更新好了 我試著不要用class, 但不知道是不是完全等價的作法

通過呼叫Symbol(key),每次生成的都是不同的Symbol,只是在呼叫toString()時,如果生成時的key一樣,輸出的字串就一樣。由於每次生成的symbol都不一樣,只要沒有暴露到外部,就無法透過他存取(除非使用Reflection),所以可以透過他來實做物件(類別)的私有成員。

well-known symbol可以透過Symbol的靜態成員來直接存取的,主要是用來定義系統內一致的一些construction,類似interface。

還是要請你仔細看一下node怎麼實做Dirent:
https://github.com/nodejs/node/blob/v12.16.2/lib/internal/fs/utils.js

kType是模組內的一個變數,而且是Symbol,所以無法再次生成,只要沒有把他Export,除了使用Reflection,也沒辦法取得。

另外,像isDirectory()等方法裡面,都是跟系統內部的常數做比對,這些常數只是在c++中定義的列舉,所以不應該依賴他的值。至於為什麼這樣做?因為...實際上去讀取目錄的是c++寫的程式,他只是在確定類型後,用他定義的列舉回傳type,所以c++端跟node核心都不會知道這些常數的內容是什麼,只是知道要跟同樣名稱的常數比對就會得到結果。

費大說得很詳細,受益良多/images/emoticon/emoticon41.gif

你寫的方法我覺得跟 Dirent 是兩回事
因為 Dirent 即使不用 Symbol ,在 construct 後的 function 依然可以透過 已知的 key 來使用
仔細看了一下
construct 中 this[kType] 設定的 Symbol 也跟 function 無關
他就只是一個常數 這個可能要再去翻 readdir 是如何傳入 type

jokie7585 iT邦新手 5 級 ‧ 2020-04-17 11:37:26 檢舉

真的很感謝兩位
原來Symbol.iterator的作法是類似interface/implement大家約定要給這個property指定一個特定功能的函式

jokie7585 iT邦新手 5 級 ‧ 2020-04-17 11:38:57 檢舉

然後Dirent裡面的kType只是透過Symbol建構式(不完整的)來建構一個symbol類型的變數用來當作Dirent的私有成員(雖然還是能透過其他方法取得)

jokie7585 iT邦新手 5 級 ‧ 2020-04-17 11:40:12 檢舉

如果我理解的正確地話真的太感謝兩位了!
感覺又更抓到了一些thinking in js的感覺/images/emoticon/emoticon02.gif

我要發表回答

立即登入回答