我取得某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}
裡面的 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);
});
})
感覺我對Symbol有很大的誤解(剛接觸js不久)
為什麼明明是Symbol的property,但卻不需要用Symbol.property呼叫?
然後我想知道為什麼console會這樣印~
希望大大賜教QQ
我試了一下,用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變數只在這個模組內部有效。
這些數字是在c++原始碼裡面定義的(我猜是列舉),node的實做也只用到常數名稱,數字的值其實不重要。
參考第四點:
https://2ality.com/2016/01/private-data-classes.html
猜測用Symbol的目的是當作私有成員之類,但是因為還是可以用Reflect.ownKeys()
取出key來取值...不是夠好的方法XD
兩位的解說真的是太詳細了QQ 小弟由衷的感謝Orz
感覺get到很多知識點
謝謝兩位!
我好像資質愚鈍...
我知道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]());
我會有這樣的疑問是因為,這些"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的字樣'
目前上面的程式碼更新好了 我試著不要用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核心都不會知道這些常數的內容是什麼,只是知道要跟同樣名稱的常數比對就會得到結果。
費大說得很詳細,受益良多
你寫的方法我覺得跟 Dirent 是兩回事
因為 Dirent 即使不用 Symbol ,在 construct 後的 function 依然可以透過 已知的 key 來使用
仔細看了一下
construct 中 this[kType] 設定的 Symbol 也跟 function 無關
他就只是一個常數 這個可能要再去翻 readdir
是如何傳入 type
的