prototype تهی چطور کد رو کند میکنه
خلاصهٔ کاملتر
نویسنده میگه سراغ یه جزئیات V8 رفته که تو Node core throughput واقعی از دستشون میداده. ماجرا از { proto: null } شروع میشه، همون الگویی که همه برای ساختن یه آبجکت شبیه map و بدون ارثبری از Object.prototype استفاده میکنن. نکته اینه که تو V8 هر آبجکتی که با prototype تهی ساخته بشه، از همون لحظهٔ ساخت میافته تو dictionary mode، یعنی propertyهاش میرن تو یه hash table به جای یه hidden class.
این دو تا هزینه داره. ساختن خود literal حدود ۵۰۰ تا ۱۵۰۰ نانوثانیه وقت میبره، در حالی که همون literal بدون proto: null حدود ۳۰ نانوثانیهست. بعدش هم هر دسترسی به property یه lookup تو هشتیبل میشه و هیچوقت inline cache نمیخوره. زیاد خوندنش هم نجاتش نمیده و تا آخر عمر کند میمونه. تنها استثنا اینه که اگه خودش به عنوان prototype یه آبجکت دیگه استفاده شه، V8 بهینهش میکنه و میبره تو fast mode.
نویسنده جدولش رو با یه بیلد از شاخهٔ main نود روی V8 نسخهٔ 14.6.202.34-node.34 و همچنین Node v24.18.0 چک کرده، با %HasFastProperties و فلگ --allow-natives-syntax. نتیجه سه جایگزین سریع میده که همچنان Object.prototype رو از مسیر lookup بیرون نگه میدارن: یه class که prototypeش خودش prototype تهی داره، یا Object.setPrototypeOf روی یه literal ساده، یا حتی literal معمولی وقتی همهٔ فیلدهایی که میخونی own property هستن.
// fast: map خود literal حفظ میشه، prototype بعدش عوض میشه
Object.setPrototypeOf({ a: 1, b: 2, c: 3 }, null);
// fast: زنجیرهٔ instance به C.prototype به null
class C { a = 1; b = 2; c = 3; }
Object.setPrototypeOf(C.prototype, null);این ماجرا تو کار پرفورمنس webstreams دو بار پیش اومد. تو راند ۱۶، که PR شمارهٔ 65625 نودجیاسه، چهار رکورد state هر استریم literal با prototype تهی بودن و تبدیلشون به class، throughput دستور pipe-to رو تقریباً دو برابر کرد، بین ۱۰۵ تا ۱۱۲ درصد. ساخت WritableStream ۲۰۴ درصد، ReadableStream ۱۳۸ درصد و TransformStream ۱۳۴ درصد سریعتر شد.
تو راند ۱۹، یعنی PR شمارهٔ 66230، دو تا sentinel سطح ماژول تو فایل lib/internal/webstreams/writablestream.js به اسم kNilRequest و kNilPendingAbortRequest مشکل داشتن. فقط یه بار ساخته میشن، ولی تو اسلاتهای درخواست write و close و abort مینشینن و فیلد promiseشون هر نوشتن چند بار خونده میشه. چون هیچوقت به عنوان prototype استفاده نمیشن، تا آخر عمر پروسه تو dictionary mode میموندن. ساختنشون به شکل literal ساده و null کردن prototype بعدش، pipe-to رو ۱۲.۳ تا ۱۴.۹ درصد بهتر کرد.
جمعبندی نویسنده اینه که prototype تهی فقط وقتی به پرفورمنس ربط داره که آبجکت روی مسیر داغ ساخته یا خونده بشه، از جمله ثابتهای مشترک و طولانیعمری که هاتپث مدام میخونه. descriptorهایی که یه بار به Object.defineProperty پاس میشن هزینهٔ قابل اندازهگیری ندارن. پس لازم نیست همهٔ proto: nullهای کدت رو بکنی بیری، فقط برو سراغ اونهایی که روی مسیری هستن که میلیون بار در ثانیه اجرا میشه.
نکات کلیدی:
- ساخت { proto: null } حدود ۵۰۰ تا ۱۵۰۰ نانوثانیه، در برابر حدود ۳۰ نانوثانیه برای همون literal بدون prototype تهی.
- خوندن property هیچوقت آبجکت رو به fast mode برنمیگردونه، فقط استفاده شدن به عنوان prototype این کارو میکنه.
- Object.setPrototypeOf({ a, b, c }, null) و نمونهٔ class با prototype تهی، هر دو fast میمونن.
- PR شمارهٔ 65625: pipe-to حدود دو برابر، ساخت WritableStream ۲۰۴ درصد سریعتر.
- PR شمارهٔ 66230: دو sentinel تو writablestream.js، سود ۱۲.۳ تا ۱۴.۹ درصدی روی pipe-to.




