[{"data":1,"prerenderedAt":1406},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjavascript-static-private-class-members":3},{"id":4,"title":5,"body":6,"description":1391,"difficulty":1392,"extension":1393,"framework":1394,"frameworkSlug":1395,"meta":1396,"navigation":134,"order":93,"path":1397,"qaPath":1398,"seo":1399,"stem":1400,"subtopic":1401,"topic":1402,"topicSlug":1403,"updated":1404,"__hash__":1405},"blog\u002Fblog\u002Fjavascript-static-private-class-members.md","JavaScript Static & Private Class Members — Fields, Methods, and True Encapsulation",{"type":7,"value":8,"toc":1376},"minimark",[9,14,32,36,43,181,188,192,199,323,347,351,354,511,514,521,531,672,694,698,703,843,853,861,868,965,971,975,982,1098,1101,1105,1116,1242,1245,1249,1262,1314,1323,1327,1369,1372],[10,11,13],"h2",{"id":12},"two-orthogonal-ideas-static-and-private","Two orthogonal ideas: static and private",[15,16,17,18,22,23,26,27,31],"p",{},"Class members can vary along two independent axes. The first is ",[19,20,21],"strong",{},"static vs instance",": does\nthe member belong to the class itself or to each instance? The second is ",[19,24,25],{},"public vs\nprivate",": can code outside the class see it? Modern JavaScript supports all combinations,\nincluding genuinely private members that the language ",[28,29,30],"em",{},"enforces"," — a big upgrade over the old\nunderscore-prefix convention. This guide covers both axes and how they combine.",[10,33,35],{"id":34},"static-members-belong-to-the-class","Static members belong to the class",[15,37,38,39,42],{},"A ",[19,40,41],{},"static"," member lives on the class itself, not on instances. You access it through the\nclass name. Static methods are typically utilities or factories that don't need a specific\ninstance.",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-js shiki shiki-themes github-light github-dark","class MathUtil {\n  static PI = 3.14159          \u002F\u002F static field\n  static square(x) { return x * x }   \u002F\u002F static method\n}\n\nMathUtil.PI            \u002F\u002F 3.14159\nMathUtil.square(5)     \u002F\u002F 25\nnew MathUtil().square  \u002F\u002F undefined — not on instances\n","js","",[51,52,53,70,91,123,129,136,148,167],"code",{"__ignoreMap":49},[54,55,58,62,66],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"szBVR","class",[54,63,65],{"class":64},"sScJk"," MathUtil",[54,67,69],{"class":68},"sVt8B"," {\n",[54,71,73,76,80,83,87],{"class":56,"line":72},2,[54,74,75],{"class":60},"  static",[54,77,79],{"class":78},"s4XuR"," PI",[54,81,82],{"class":60}," =",[54,84,86],{"class":85},"sj4cs"," 3.14159",[54,88,90],{"class":89},"sJ8bj","          \u002F\u002F static field\n",[54,92,94,96,99,102,105,108,111,114,117,120],{"class":56,"line":93},3,[54,95,75],{"class":60},[54,97,98],{"class":64}," square",[54,100,101],{"class":68},"(",[54,103,104],{"class":78},"x",[54,106,107],{"class":68},") { ",[54,109,110],{"class":60},"return",[54,112,113],{"class":68}," x ",[54,115,116],{"class":60},"*",[54,118,119],{"class":68}," x }   ",[54,121,122],{"class":89},"\u002F\u002F static method\n",[54,124,126],{"class":56,"line":125},4,[54,127,128],{"class":68},"}\n",[54,130,132],{"class":56,"line":131},5,[54,133,135],{"emptyLinePlaceholder":134},true,"\n",[54,137,139,142,145],{"class":56,"line":138},6,[54,140,141],{"class":68},"MathUtil.",[54,143,144],{"class":85},"PI",[54,146,147],{"class":89},"            \u002F\u002F 3.14159\n",[54,149,151,153,156,158,161,164],{"class":56,"line":150},7,[54,152,141],{"class":68},[54,154,155],{"class":64},"square",[54,157,101],{"class":68},[54,159,160],{"class":85},"5",[54,162,163],{"class":68},")     ",[54,165,166],{"class":89},"\u002F\u002F 25\n",[54,168,170,173,175,178],{"class":56,"line":169},8,[54,171,172],{"class":60},"new",[54,174,65],{"class":64},[54,176,177],{"class":68},"().square  ",[54,179,180],{"class":89},"\u002F\u002F undefined — not on instances\n",[15,182,183,184,187],{},"Static members are perfect for constants, helper functions, and ",[19,185,186],{},"factory methods"," that\nconstruct instances in controlled ways.",[10,189,191],{"id":190},"the-factory-method-pattern","The factory-method pattern",[15,193,194,195,198],{},"Because ",[51,196,197],{},"this"," inside a static method refers to the class it was called on, static factories\ncompose beautifully with inheritance.",[44,200,202],{"className":46,"code":201,"language":48,"meta":49,"style":49},"class Model {\n  constructor(data) { this.data = data }\n  static fromJSON(json) { return new this(JSON.parse(json)) }  \u002F\u002F `this` = calling class\n}\nclass User extends Model {}\n\nUser.fromJSON('{\"name\":\"Ada\"}') instanceof User   \u002F\u002F true\n",[51,203,204,213,236,275,279,294,298],{"__ignoreMap":49},[54,205,206,208,211],{"class":56,"line":57},[54,207,61],{"class":60},[54,209,210],{"class":64}," Model",[54,212,69],{"class":68},[54,214,215,218,220,223,225,227,230,233],{"class":56,"line":72},[54,216,217],{"class":60},"  constructor",[54,219,101],{"class":68},[54,221,222],{"class":78},"data",[54,224,107],{"class":68},[54,226,197],{"class":85},[54,228,229],{"class":68},".data ",[54,231,232],{"class":60},"=",[54,234,235],{"class":68}," data }\n",[54,237,238,240,243,245,248,250,252,255,258,260,263,266,269,272],{"class":56,"line":93},[54,239,75],{"class":60},[54,241,242],{"class":64}," fromJSON",[54,244,101],{"class":68},[54,246,247],{"class":78},"json",[54,249,107],{"class":68},[54,251,110],{"class":60},[54,253,254],{"class":60}," new",[54,256,257],{"class":85}," this",[54,259,101],{"class":68},[54,261,262],{"class":85},"JSON",[54,264,265],{"class":68},".",[54,267,268],{"class":64},"parse",[54,270,271],{"class":68},"(json)) }  ",[54,273,274],{"class":89},"\u002F\u002F `this` = calling class\n",[54,276,277],{"class":56,"line":125},[54,278,128],{"class":68},[54,280,281,283,286,289,291],{"class":56,"line":131},[54,282,61],{"class":60},[54,284,285],{"class":64}," User",[54,287,288],{"class":60}," extends",[54,290,210],{"class":64},[54,292,293],{"class":68}," {}\n",[54,295,296],{"class":56,"line":138},[54,297,135],{"emptyLinePlaceholder":134},[54,299,300,303,306,308,312,315,318,320],{"class":56,"line":150},[54,301,302],{"class":68},"User.",[54,304,305],{"class":64},"fromJSON",[54,307,101],{"class":68},[54,309,311],{"class":310},"sZZnC","'{\"name\":\"Ada\"}'",[54,313,314],{"class":68},") ",[54,316,317],{"class":60},"instanceof",[54,319,285],{"class":64},[54,321,322],{"class":89},"   \u002F\u002F true\n",[15,324,325,328,329,332,333,336,337,339,340,342,343,346],{},[51,326,327],{},"new this(...)"," builds a ",[51,330,331],{},"User"," when called as ",[51,334,335],{},"User.fromJSON",", because ",[51,338,197],{}," is ",[51,341,331],{},", not\n",[51,344,345],{},"Model",". This is a common, powerful pattern for ORMs and value objects.",[10,348,350],{"id":349},"accessing-instances-from-static-methods","Accessing instances from static methods",[15,352,353],{},"A frequent use of static methods is operating across instances — comparisons, aggregations,\nor counting.",[44,355,357],{"className":46,"code":356,"language":48,"meta":49,"style":49},"class Point {\n  constructor(x, y) { this.x = x; this.y = y }\n  static distance(a, b) {\n    return Math.hypot(a.x - b.x, a.y - b.y)   \u002F\u002F takes two instances\n  }\n}\n\nPoint.distance(new Point(0, 0), new Point(3, 4))   \u002F\u002F 5\n",[51,358,359,368,404,424,452,457,461,465],{"__ignoreMap":49},[54,360,361,363,366],{"class":56,"line":57},[54,362,61],{"class":60},[54,364,365],{"class":64}," Point",[54,367,69],{"class":68},[54,369,370,372,374,376,379,382,384,386,389,391,394,396,399,401],{"class":56,"line":72},[54,371,217],{"class":60},[54,373,101],{"class":68},[54,375,104],{"class":78},[54,377,378],{"class":68},", ",[54,380,381],{"class":78},"y",[54,383,107],{"class":68},[54,385,197],{"class":85},[54,387,388],{"class":68},".x ",[54,390,232],{"class":60},[54,392,393],{"class":68}," x; ",[54,395,197],{"class":85},[54,397,398],{"class":68},".y ",[54,400,232],{"class":60},[54,402,403],{"class":68}," y }\n",[54,405,406,408,411,413,416,418,421],{"class":56,"line":93},[54,407,75],{"class":60},[54,409,410],{"class":64}," distance",[54,412,101],{"class":68},[54,414,415],{"class":78},"a",[54,417,378],{"class":68},[54,419,420],{"class":78},"b",[54,422,423],{"class":68},") {\n",[54,425,426,429,432,435,438,441,444,446,449],{"class":56,"line":125},[54,427,428],{"class":60},"    return",[54,430,431],{"class":68}," Math.",[54,433,434],{"class":64},"hypot",[54,436,437],{"class":68},"(a.x ",[54,439,440],{"class":60},"-",[54,442,443],{"class":68}," b.x, a.y ",[54,445,440],{"class":60},[54,447,448],{"class":68}," b.y)   ",[54,450,451],{"class":89},"\u002F\u002F takes two instances\n",[54,453,454],{"class":56,"line":131},[54,455,456],{"class":68},"  }\n",[54,458,459],{"class":56,"line":138},[54,460,128],{"class":68},[54,462,463],{"class":56,"line":150},[54,464,135],{"emptyLinePlaceholder":134},[54,466,467,470,473,475,477,479,481,484,486,488,491,493,495,497,500,502,505,508],{"class":56,"line":169},[54,468,469],{"class":68},"Point.",[54,471,472],{"class":64},"distance",[54,474,101],{"class":68},[54,476,172],{"class":60},[54,478,365],{"class":64},[54,480,101],{"class":68},[54,482,483],{"class":85},"0",[54,485,378],{"class":68},[54,487,483],{"class":85},[54,489,490],{"class":68},"), ",[54,492,172],{"class":60},[54,494,365],{"class":64},[54,496,101],{"class":68},[54,498,499],{"class":85},"3",[54,501,378],{"class":68},[54,503,504],{"class":85},"4",[54,506,507],{"class":68},"))   ",[54,509,510],{"class":89},"\u002F\u002F 5\n",[15,512,513],{},"This reads naturally as a class-level operation rather than awkwardly living on one instance.",[10,515,517,518],{"id":516},"private-fields-with","Private fields with ",[51,519,520],{},"#",[15,522,523,524,526,527,530],{},"The ",[51,525,520],{}," prefix declares a ",[19,528,529],{},"truly private"," field — enforced by the language, not by\nconvention. Private members are inaccessible from outside the class body; attempting to read\none is a syntax error.",[44,532,534],{"className":46,"code":533,"language":48,"meta":49,"style":49},"class BankAccount {\n  #balance = 0                  \u002F\u002F private field\n\n  deposit(amount) { this.#balance += amount; return this.#balance }\n  getBalance() { return this.#balance }\n}\n\nconst acc = new BankAccount()\nacc.deposit(100)     \u002F\u002F 100\nacc.getBalance()     \u002F\u002F 100\nacc.#balance         \u002F\u002F SyntaxError — private, even at runtime\n",[51,535,536,545,558,562,592,606,610,614,631,650,663],{"__ignoreMap":49},[54,537,538,540,543],{"class":56,"line":57},[54,539,61],{"class":60},[54,541,542],{"class":64}," BankAccount",[54,544,69],{"class":68},[54,546,547,550,552,555],{"class":56,"line":72},[54,548,549],{"class":78},"  #balance",[54,551,82],{"class":60},[54,553,554],{"class":85}," 0",[54,556,557],{"class":89},"                  \u002F\u002F private field\n",[54,559,560],{"class":56,"line":93},[54,561,135],{"emptyLinePlaceholder":134},[54,563,564,567,569,572,574,576,579,582,585,587,589],{"class":56,"line":125},[54,565,566],{"class":64},"  deposit",[54,568,101],{"class":68},[54,570,571],{"class":78},"amount",[54,573,107],{"class":68},[54,575,197],{"class":85},[54,577,578],{"class":68},".#balance ",[54,580,581],{"class":60},"+=",[54,583,584],{"class":68}," amount; ",[54,586,110],{"class":60},[54,588,257],{"class":85},[54,590,591],{"class":68},".#balance }\n",[54,593,594,597,600,602,604],{"class":56,"line":131},[54,595,596],{"class":64},"  getBalance",[54,598,599],{"class":68},"() { ",[54,601,110],{"class":60},[54,603,257],{"class":85},[54,605,591],{"class":68},[54,607,608],{"class":56,"line":138},[54,609,128],{"class":68},[54,611,612],{"class":56,"line":150},[54,613,135],{"emptyLinePlaceholder":134},[54,615,616,619,622,624,626,628],{"class":56,"line":169},[54,617,618],{"class":60},"const",[54,620,621],{"class":85}," acc",[54,623,82],{"class":60},[54,625,254],{"class":60},[54,627,542],{"class":64},[54,629,630],{"class":68},"()\n",[54,632,634,637,640,642,645,647],{"class":56,"line":633},9,[54,635,636],{"class":68},"acc.",[54,638,639],{"class":64},"deposit",[54,641,101],{"class":68},[54,643,644],{"class":85},"100",[54,646,163],{"class":68},[54,648,649],{"class":89},"\u002F\u002F 100\n",[54,651,653,655,658,661],{"class":56,"line":652},10,[54,654,636],{"class":68},[54,656,657],{"class":64},"getBalance",[54,659,660],{"class":68},"()     ",[54,662,649],{"class":89},[54,664,666,669],{"class":56,"line":665},11,[54,667,668],{"class":68},"acc.#balance         ",[54,670,671],{"class":89},"\u002F\u002F SyntaxError — private, even at runtime\n",[15,673,674,675,678,679,682,683,378,686,689,690,693],{},"Unlike the old ",[51,676,677],{},"_balance"," convention (which anyone could read or write), ",[51,680,681],{},"#balance"," is\ngenuinely hidden. It won't appear in ",[51,684,685],{},"Object.keys",[51,687,688],{},"JSON.stringify",", or ",[51,691,692],{},"for...in",", and it\ncan't be accessed via bracket notation either.",[10,695,697],{"id":696},"private-methods-and-accessors","Private methods and accessors",[15,699,523,700,702],{},[51,701,520],{}," syntax extends to methods and getters\u002Fsetters, letting you hide internal helpers.",[44,704,706],{"className":46,"code":705,"language":48,"meta":49,"style":49},"class Temperature {\n  #celsius = 0\n  #validate(c) {                 \u002F\u002F private method\n    if (c \u003C -273.15) throw new RangeError('below absolute zero')\n    return c\n  }\n  set value(c) { this.#celsius = this.#validate(c) }\n  get value() { return this.#celsius }\n}\n",[51,707,708,717,727,746,781,788,792,823,839],{"__ignoreMap":49},[54,709,710,712,715],{"class":56,"line":57},[54,711,61],{"class":60},[54,713,714],{"class":64}," Temperature",[54,716,69],{"class":68},[54,718,719,722,724],{"class":56,"line":72},[54,720,721],{"class":78},"  #celsius",[54,723,82],{"class":60},[54,725,726],{"class":85}," 0\n",[54,728,729,732,735,737,740,743],{"class":56,"line":93},[54,730,731],{"class":68},"  #",[54,733,734],{"class":64},"validate",[54,736,101],{"class":68},[54,738,739],{"class":78},"c",[54,741,742],{"class":68},") {                 ",[54,744,745],{"class":89},"\u002F\u002F private method\n",[54,747,748,751,754,757,760,763,765,768,770,773,775,778],{"class":56,"line":125},[54,749,750],{"class":60},"    if",[54,752,753],{"class":68}," (c ",[54,755,756],{"class":60},"\u003C",[54,758,759],{"class":60}," -",[54,761,762],{"class":85},"273.15",[54,764,314],{"class":68},[54,766,767],{"class":60},"throw",[54,769,254],{"class":60},[54,771,772],{"class":64}," RangeError",[54,774,101],{"class":68},[54,776,777],{"class":310},"'below absolute zero'",[54,779,780],{"class":68},")\n",[54,782,783,785],{"class":56,"line":131},[54,784,428],{"class":60},[54,786,787],{"class":68}," c\n",[54,789,790],{"class":56,"line":138},[54,791,456],{"class":68},[54,793,794,797,800,802,804,806,808,811,813,815,817,820],{"class":56,"line":150},[54,795,796],{"class":60},"  set",[54,798,799],{"class":64}," value",[54,801,101],{"class":68},[54,803,739],{"class":78},[54,805,107],{"class":68},[54,807,197],{"class":85},[54,809,810],{"class":68},".#celsius ",[54,812,232],{"class":60},[54,814,257],{"class":85},[54,816,265],{"class":68},[54,818,819],{"class":64},"#validate",[54,821,822],{"class":68},"(c) }\n",[54,824,825,828,830,832,834,836],{"class":56,"line":169},[54,826,827],{"class":60},"  get",[54,829,799],{"class":64},[54,831,599],{"class":68},[54,833,110],{"class":60},[54,835,257],{"class":85},[54,837,838],{"class":68},".#celsius }\n",[54,840,841],{"class":56,"line":633},[54,842,128],{"class":68},[15,844,845,846,849,850,852],{},"Private methods keep your public API clean — consumers see only ",[51,847,848],{},"value",", while ",[51,851,819],{},"\nstays an internal implementation detail you're free to change.",[10,854,856,857,860],{"id":855},"checking-for-a-private-field-the-in-trick","Checking for a private field — the ",[51,858,859],{},"in"," trick",[15,862,863,864,867],{},"You can test whether an object has a particular private field using ",[51,865,866],{},"#field in obj",". This is\nthe idiomatic \"brand check\" to confirm an object is a real instance of your class.",[44,869,871],{"className":46,"code":870,"language":48,"meta":49,"style":49},"class Stack {\n  #items = []\n  static isStack(obj) {\n    return #items in obj   \u002F\u002F true only for genuine Stack instances\n  }\n}\n\nStack.isStack(new Stack())   \u002F\u002F true\nStack.isStack({})            \u002F\u002F false\n",[51,872,873,882,892,906,921,925,929,933,953],{"__ignoreMap":49},[54,874,875,877,880],{"class":56,"line":57},[54,876,61],{"class":60},[54,878,879],{"class":64}," Stack",[54,881,69],{"class":68},[54,883,884,887,889],{"class":56,"line":72},[54,885,886],{"class":78},"  #items",[54,888,82],{"class":60},[54,890,891],{"class":68}," []\n",[54,893,894,896,899,901,904],{"class":56,"line":93},[54,895,75],{"class":60},[54,897,898],{"class":64}," isStack",[54,900,101],{"class":68},[54,902,903],{"class":78},"obj",[54,905,423],{"class":68},[54,907,908,910,913,915,918],{"class":56,"line":125},[54,909,428],{"class":60},[54,911,912],{"class":68}," #items ",[54,914,859],{"class":60},[54,916,917],{"class":68}," obj   ",[54,919,920],{"class":89},"\u002F\u002F true only for genuine Stack instances\n",[54,922,923],{"class":56,"line":131},[54,924,456],{"class":68},[54,926,927],{"class":56,"line":138},[54,928,128],{"class":68},[54,930,931],{"class":56,"line":150},[54,932,135],{"emptyLinePlaceholder":134},[54,934,935,938,941,943,945,947,950],{"class":56,"line":169},[54,936,937],{"class":68},"Stack.",[54,939,940],{"class":64},"isStack",[54,942,101],{"class":68},[54,944,172],{"class":60},[54,946,879],{"class":64},[54,948,949],{"class":68},"())   ",[54,951,952],{"class":89},"\u002F\u002F true\n",[54,954,955,957,959,962],{"class":56,"line":633},[54,956,937],{"class":68},[54,958,940],{"class":64},[54,960,961],{"class":68},"({})            ",[54,963,964],{"class":89},"\u002F\u002F false\n",[15,966,967,968,970],{},"This is more robust than ",[51,969,317],{}," for confirming an object actually went through your\nconstructor, since private fields can only be installed by the class itself.",[10,972,974],{"id":973},"static-private-members","Static private members",[15,976,977,978,981],{},"You can combine both axes: ",[19,979,980],{},"static private"," fields and methods belong to the class and are\nhidden from outside. They're ideal for shared internal state like caches or instance\nregistries.",[44,983,985],{"className":46,"code":984,"language":48,"meta":49,"style":49},"class IdGenerator {\n  static #counter = 0           \u002F\u002F static private field\n  static #next() { return ++IdGenerator.#counter }  \u002F\u002F static private method\n\n  static create() { return { id: IdGenerator.#next() } }\n}\n\nIdGenerator.create()   \u002F\u002F { id: 1 }\nIdGenerator.create()   \u002F\u002F { id: 2 }\nIdGenerator.#counter   \u002F\u002F SyntaxError — private\n",[51,986,987,996,1010,1033,1037,1057,1061,1065,1079,1090],{"__ignoreMap":49},[54,988,989,991,994],{"class":56,"line":57},[54,990,61],{"class":60},[54,992,993],{"class":64}," IdGenerator",[54,995,69],{"class":68},[54,997,998,1000,1003,1005,1007],{"class":56,"line":72},[54,999,75],{"class":60},[54,1001,1002],{"class":78}," #counter",[54,1004,82],{"class":60},[54,1006,554],{"class":85},[54,1008,1009],{"class":89},"           \u002F\u002F static private field\n",[54,1011,1012,1014,1017,1020,1022,1024,1027,1030],{"class":56,"line":93},[54,1013,75],{"class":60},[54,1015,1016],{"class":68}," #",[54,1018,1019],{"class":64},"next",[54,1021,599],{"class":68},[54,1023,110],{"class":60},[54,1025,1026],{"class":60}," ++",[54,1028,1029],{"class":68},"IdGenerator.#counter }  ",[54,1031,1032],{"class":89},"\u002F\u002F static private method\n",[54,1034,1035],{"class":56,"line":125},[54,1036,135],{"emptyLinePlaceholder":134},[54,1038,1039,1041,1044,1046,1048,1051,1054],{"class":56,"line":131},[54,1040,75],{"class":60},[54,1042,1043],{"class":64}," create",[54,1045,599],{"class":68},[54,1047,110],{"class":60},[54,1049,1050],{"class":68}," { id: IdGenerator.",[54,1052,1053],{"class":64},"#next",[54,1055,1056],{"class":68},"() } }\n",[54,1058,1059],{"class":56,"line":138},[54,1060,128],{"class":68},[54,1062,1063],{"class":56,"line":150},[54,1064,135],{"emptyLinePlaceholder":134},[54,1066,1067,1070,1073,1076],{"class":56,"line":169},[54,1068,1069],{"class":68},"IdGenerator.",[54,1071,1072],{"class":64},"create",[54,1074,1075],{"class":68},"()   ",[54,1077,1078],{"class":89},"\u002F\u002F { id: 1 }\n",[54,1080,1081,1083,1085,1087],{"class":56,"line":633},[54,1082,1069],{"class":68},[54,1084,1072],{"class":64},[54,1086,1075],{"class":68},[54,1088,1089],{"class":89},"\u002F\u002F { id: 2 }\n",[54,1091,1092,1095],{"class":56,"line":652},[54,1093,1094],{"class":68},"IdGenerator.#counter   ",[54,1096,1097],{"class":89},"\u002F\u002F SyntaxError — private\n",[15,1099,1100],{},"The counter is encapsulated entirely within the class; no external code can read or tamper\nwith it.",[10,1102,1104],{"id":1103},"static-initialization-blocks","Static initialization blocks",[15,1106,1107,1108,1111,1112,1115],{},"Sometimes static setup needs more than a single expression — multiple statements, try\u002Fcatch,\nor access to private static fields. A ",[19,1109,1110],{},"static block"," (",[51,1113,1114],{},"static { ... }",") runs once when the\nclass is defined.",[44,1117,1119],{"className":46,"code":1118,"language":48,"meta":49,"style":49},"class Config {\n  static #settings = {}\n  static {\n    const raw = loadConfigFile()\n    for (const [k, v] of Object.entries(raw)) {\n      Config.#settings[k] = v   \u002F\u002F can touch private statics during setup\n    }\n  }\n  static get(key) { return Config.#settings[key] }\n}\n",[51,1120,1121,1130,1141,1147,1162,1197,1210,1215,1219,1238],{"__ignoreMap":49},[54,1122,1123,1125,1128],{"class":56,"line":57},[54,1124,61],{"class":60},[54,1126,1127],{"class":64}," Config",[54,1129,69],{"class":68},[54,1131,1132,1134,1137,1139],{"class":56,"line":72},[54,1133,75],{"class":60},[54,1135,1136],{"class":78}," #settings",[54,1138,82],{"class":60},[54,1140,293],{"class":68},[54,1142,1143,1145],{"class":56,"line":93},[54,1144,75],{"class":60},[54,1146,69],{"class":68},[54,1148,1149,1152,1155,1157,1160],{"class":56,"line":125},[54,1150,1151],{"class":60},"    const",[54,1153,1154],{"class":85}," raw",[54,1156,82],{"class":60},[54,1158,1159],{"class":64}," loadConfigFile",[54,1161,630],{"class":68},[54,1163,1164,1167,1169,1171,1174,1177,1179,1182,1185,1188,1191,1194],{"class":56,"line":131},[54,1165,1166],{"class":60},"    for",[54,1168,1111],{"class":68},[54,1170,618],{"class":60},[54,1172,1173],{"class":68}," [",[54,1175,1176],{"class":85},"k",[54,1178,378],{"class":68},[54,1180,1181],{"class":85},"v",[54,1183,1184],{"class":68},"] ",[54,1186,1187],{"class":60},"of",[54,1189,1190],{"class":68}," Object.",[54,1192,1193],{"class":64},"entries",[54,1195,1196],{"class":68},"(raw)) {\n",[54,1198,1199,1202,1204,1207],{"class":56,"line":138},[54,1200,1201],{"class":68},"      Config.#settings[k] ",[54,1203,232],{"class":60},[54,1205,1206],{"class":68}," v   ",[54,1208,1209],{"class":89},"\u002F\u002F can touch private statics during setup\n",[54,1211,1212],{"class":56,"line":150},[54,1213,1214],{"class":68},"    }\n",[54,1216,1217],{"class":56,"line":169},[54,1218,456],{"class":68},[54,1220,1221,1223,1226,1228,1231,1233,1235],{"class":56,"line":633},[54,1222,75],{"class":60},[54,1224,1225],{"class":64}," get",[54,1227,101],{"class":68},[54,1229,1230],{"class":78},"key",[54,1232,107],{"class":68},[54,1234,110],{"class":60},[54,1236,1237],{"class":68}," Config.#settings[key] }\n",[54,1239,1240],{"class":56,"line":652},[54,1241,128],{"class":68},[15,1243,1244],{},"Static blocks run in source order alongside static field initializers, giving you a clean\nplace for complex class-level initialization.",[10,1246,1248],{"id":1247},"encapsulation-enforced-vs-convention","Encapsulation: enforced vs convention",[15,1250,1251,1252,1254,1255,1257,1258,1261],{},"Before ",[51,1253,520],{},", the community used a leading underscore (",[51,1256,677],{},") to ",[28,1259,1260],{},"signal"," \"please don't\ntouch.\" It was a gentleman's agreement — nothing stopped access.",[44,1263,1265],{"className":46,"code":1264,"language":48,"meta":49,"style":49},"class Old {\n  constructor() { this._secret = 42 }   \u002F\u002F convention only fully accessible\n}\nnew Old()._secret   \u002F\u002F 42 — anyone can read\u002Fwrite it\n",[51,1266,1267,1276,1298,1302],{"__ignoreMap":49},[54,1268,1269,1271,1274],{"class":56,"line":57},[54,1270,61],{"class":60},[54,1272,1273],{"class":64}," Old",[54,1275,69],{"class":68},[54,1277,1278,1280,1282,1284,1287,1289,1292,1295],{"class":56,"line":72},[54,1279,217],{"class":60},[54,1281,599],{"class":68},[54,1283,197],{"class":85},[54,1285,1286],{"class":68},"._secret ",[54,1288,232],{"class":60},[54,1290,1291],{"class":85}," 42",[54,1293,1294],{"class":68}," }   ",[54,1296,1297],{"class":89},"\u002F\u002F convention only fully accessible\n",[54,1299,1300],{"class":56,"line":93},[54,1301,128],{"class":68},[54,1303,1304,1306,1308,1311],{"class":56,"line":125},[54,1305,172],{"class":60},[54,1307,1273],{"class":64},[54,1309,1310],{"class":68},"()._secret   ",[54,1312,1313],{"class":89},"\u002F\u002F 42 — anyone can read\u002Fwrite it\n",[15,1315,1316,1317,1319,1320,1322],{},"True ",[51,1318,520],{}," privacy changes the contract: the language guarantees outsiders can't reach in.\nThis matters for libraries (consumers can't depend on internals) and for invariants (state\ncan't be corrupted from outside). Prefer ",[51,1321,520],{}," for anything that should be genuinely internal;\nreserve the underscore convention for legacy code or quick scripts.",[10,1324,1326],{"id":1325},"key-takeaways","Key takeaways",[1328,1329,1330,1340,1349,1352,1357,1360],"ul",{},[1331,1332,1333,1336,1337,1339],"li",{},[19,1334,1335],{},"Static"," members live on the class, not instances — great for constants, utilities, and\nfactory methods where ",[51,1338,197],{}," is the calling class.",[1331,1341,1342,1345,1346,1348],{},[19,1343,1344],{},"Private"," members use the ",[51,1347,520],{}," prefix and are enforced by the language: invisible to\nenumeration, serialization, and external code.",[1331,1350,1351],{},"Private methods and accessors keep the public API minimal while hiding implementation.",[1331,1353,1354,1356],{},[51,1355,866],{}," is a reliable brand check that an object is a true instance.",[1331,1358,1359],{},"Static private members encapsulate shared class state like counters and caches; static\nblocks handle complex one-time setup.",[1331,1361,1362,1364,1365,1368],{},[51,1363,520],{}," provides real encapsulation, unlike the old ",[51,1366,1367],{},"_underscore"," convention which was only a\nhint.",[15,1370,1371],{},"Together, static and private members let you design classes with a clean public surface and a\nprotected internal core — the foundation of robust object-oriented JavaScript.",[1373,1374,1375],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":49,"searchDepth":72,"depth":72,"links":1377},[1378,1379,1380,1381,1382,1384,1385,1387,1388,1389,1390],{"id":12,"depth":72,"text":13},{"id":34,"depth":72,"text":35},{"id":190,"depth":72,"text":191},{"id":349,"depth":72,"text":350},{"id":516,"depth":72,"text":1383},"Private fields with #",{"id":696,"depth":72,"text":697},{"id":855,"depth":72,"text":1386},"Checking for a private field — the in trick",{"id":973,"depth":72,"text":974},{"id":1103,"depth":72,"text":1104},{"id":1247,"depth":72,"text":1248},{"id":1325,"depth":72,"text":1326},"Learn static and private members in JavaScript classes — static fields and methods, the hash private syntax, private methods, static blocks, and real encapsulation versus convention.","medium","md","JavaScript","javascript",{},"\u002Fblog\u002Fjavascript-static-private-class-members","\u002Fjavascript\u002Fclasses\u002Fstatic-private",{"title":5,"description":1391},"blog\u002Fjavascript-static-private-class-members","Static & Private Members","Classes & OOP","classes","2026-06-18","HqVds0GrJWJN8__628HtYj-_dScp1_aEERvw1kRALT0",1781808673080]