{"id":13847223,"url":"https://github.com/trusktr/lowclass","last_synced_at":"2025-05-01T09:54:33.079Z","repository":{"id":57380340,"uuid":"42763542","full_name":"trusktr/lowclass","owner":"trusktr","description":"JavaScript/TypeScript class inheritance tools.","archived":false,"fork":false,"pushed_at":"2024-10-02T21:12:27.000Z","size":1104,"stargazers_count":28,"open_issues_count":10,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-01T09:54:20.450Z","etag":null,"topics":["classes","classes-and-inheritance","javascript","meta-programming","metaprogramming","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trusktr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-19T07:07:06.000Z","updated_at":"2024-10-02T21:12:31.000Z","dependencies_parsed_at":"2023-10-15T16:43:48.971Z","dependency_job_id":"8686fca8-acd0-4368-9ed8-b73b4c4177e0","html_url":"https://github.com/trusktr/lowclass","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trusktr%2Flowclass","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trusktr%2Flowclass/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trusktr%2Flowclass/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trusktr%2Flowclass/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trusktr","download_url":"https://codeload.github.com/trusktr/lowclass/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251856993,"owners_count":21655119,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["classes","classes-and-inheritance","javascript","meta-programming","metaprogramming","typescript"],"created_at":"2024-08-04T18:01:13.715Z","updated_at":"2025-05-01T09:54:33.060Z","avatar_url":"https://github.com/trusktr.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# lowclass\n\nJavaScript/TypeScript class inheritance tools.\n\nLowclass is a lib that includes the following inheritance tools:\n\n- A `multiple()` function for composing ES2015 `class`es together in a simple\n  ergonomic way. For example:\n\n  ```js\n  import {multiple} from 'lowclass/dist/multiple.js'\n\n  // define a few classes with unique features:\n  class Walker {\n  \twalk() {\n  \t\t/* feet move */\n  \t}\n  }\n  class Talker {\n  \ttalk() {\n  \t\t/* hello */\n  \t}\n  }\n  class Barker {\n  \tbark() {\n  \t\t/* woof */\n  \t}\n  }\n  class Attacker {\n  \tattack() {\n  \t\t/* boom */\n  \t}\n  }\n\n  // Now use them like regular classes by extending from them normally:\n\n  class StarWarsATATWalker extends Walker {\n  \tfireLaser() {\n  \t\t/* ... */\n  \t}\n  }\n\n  const atat = new StarWarsATATWalker()\n  atat.walk()\n  atat.fireLaser()\n\n  // Or compose them together:\n\n  class Dog extends multiple(Walker, Barker, Attacker) {\n  \tlick() {\n  \t\t/* ... */\n  \t}\n  }\n\n  const dog = new Dog()\n  dog.lick()\n  dog.walk()\n  dog.bark()\n  dog.attack()\n\n  class Human extends multiple(Talker, Attacker, Walker) {\n  \tyell() {\n  \t\t/* Hey! */\n  \t}\n  }\n\n  const person = new Human()\n  person.yell()\n  person.walk()\n  person.talk()\n  person.attack()\n  ```\n\n- A `Mixin()` helper for making mixable ES2015 `class`es. Mixins are less\n  ergonomic than composing classes with the `multiple()` helper, but if you're\n  after performance, then mixins (made with or without the `Mixin()` helper) will have\n  faster instantiation and property lookup than classes composed with\n  `multiple()`. For example:\n\n  ```js\n  import {Mixin} from 'lowclass/dist/Mixin.js'\n\n  // define a few \"class-factory mixins\":\n  const Walker = Mixin(Base =\u003e {\n  \treturn class Walker extends Base {\n  \t\twalk() {\n  \t\t\t/* feet move */\n  \t\t}\n  \t}\n  })\n  const Talker = Mixin(Base =\u003e {\n  \treturn class Talker extends Base {\n  \t\ttalk() {\n  \t\t\t/* hello */\n  \t\t}\n  \t}\n  })\n  const Barker = Mixin(Base =\u003e {\n  \treturn class extends Base {\n  \t\tbark() {\n  \t\t\t/* woof */\n  \t\t}\n  \t}\n  })\n  const Attacker = Mixin(Base =\u003e {\n  \treturn class extends Base {\n  \t\tattack() {\n  \t\t\t/* boom */\n  \t\t}\n  \t}\n  })\n\n  // At this point Walker, Talker, and Barker are references to ES2015 `class`es.\n\n  // Now use them like regular classes by extending from them normally:\n\n  class StarWarsATATWalker extends Walker {\n  \tfireLaser() {\n  \t\t/* ... */\n  \t}\n  }\n\n  const atat = new StarWarsATATWalker()\n  atat.walk()\n  atat.fireLaser()\n\n  // Or mix them together to compose features together:\n\n  class Dog extends Walker.mixin(Barker.mixin(Attacker)) {\n  \tlick() {\n  \t\t/* ... */\n  \t}\n  }\n\n  const dog = new Dog()\n  dog.lick()\n  dog.walk()\n  dog.bark()\n  dog.attack()\n\n  class Human extends Talker.mixin(Attacker.mixin(Walker)) {\n  \tyell() {\n  \t\t/* Hey! */\n  \t}\n  }\n\n  const person = new Human()\n  person.yell()\n  person.walk()\n  person.talk()\n  person.attack()\n  ```\n\n- A `Class()` tool for creating classes with public, protected, and private members. For example:\n\n  ```js\n  import Class from 'lowclass/dist/Class.js'\n  import Something from 'somewhere'\n\n  export default Class('Thing').extends(Something, ({Protected, Private}) =\u003e ({\n      doSomething() {\n          // this method is public\n          Protected(this).makeStuff()\n      }\n\n      protected: {\n          makeStuff() {\n              // this method is protected\n              Private(this).stuffImpl()\n          }\n      },\n\n      private: {\n          stuffImpl() {\n              // this method is private\n          }\n      }\n  }))\n  ```\n\n  ```js\n  import Thing from './Thing'\n\n  const Blob = Class('Blob').extends(Thing, ({Super, Protected, Private}) =\u003e ({\n  \tdoSomething() {\n  \t\tSuper(this).doSomething() // works fine, makeStuff is public.\n  \t\tProtected(this).makeStuff() // works fine, makeStuff is protected and inherited\n\n  \t\t// logs \"undefined\", private methods are not inherited\n  \t\tconsole.log(this.stuffImpl)\n\n  \t\t// try to access it with the Private helper:\n  \t\tPrivate(this).stuffImpl() // error, can not read property \"stuffImpl\" of undefined.\n  \t},\n  }))\n\n  const blob = new Blob()\n  // access public members:\n  blob.doSomething() // it works\n\n  // can not acecss protected or private members:\n  blob.makeStuff() // error, can not read property \"makeStuff\" of undefined.\n  blob.stuffImpl() // error, can not read property \"stuffImpl\" of undefined.\n  ```\n\n#### `npm install lowclass --save`\n\nLowclass let's us define classes with protected and private data similar to in\nC++ (and similar to some some extent Java):\n\n- `Public` members can be accessed from outside the class.\n- `Protected` members can be accessed in the class and its derived classes.\n- `Private` members can be only accessed within the class.\n\nBut there's an interesting difference (advantage) that lowclass private members\nhave over C++ private members: private functionality of a class made with\nlowclass can be inherited by a derived subclass, but the functionality is still\nscoped to the class where it is utilized, meaning that the inherited\nfunctionality will operate on the private data of the class where the inherited\nfunctionality is used without breaking private, protected, and public API\ncontracts.\n\nLowclass supports\n\n- extending builtins like Array. (see\n  [`tests/extending-builtins.test.js`](./tests/extending-builtins.test.js)).\n- extending native ES6 classes. (see\n  [`tests/extending-native-classes.test.js`](./tests/extending-native-classes.test.js))\n- extending builtins like `HTMLElement` and using the subclasses in native APIs\n  like Custom Elements. (see\n  [`tests/custom-elements.test.js`](./tests/custom-elements.test.js)).\n\n## Intro\n\nAll of the intro examples are available as tests in\n[`tests/readme-examples.test.js`](./tests/readme-examples.test.js), and\nthe other test files contain many more examples.\n\n### Hiding members of your existing classes\n\nYou may already be using ES2015's native `class` syntax to define your classes,\nfor example:\n\n```js\nclass Thing {\n\tconstructor() {\n\t\t// you might be using a convention like leading underscores to\n\t\t// tell people some property is \"protected\" or \"private\"\n\t\tthis._protectedProperty = 'yoohoo'\n\t}\n\n\tsomeMethod() {\n\t\treturn this._protectedProperty\n\t}\n}\n\nconst instance = new Thing()\n\ninstance.someMethod() // returns \"yoohoo\"\n\n// but the property is not actually protected:\nconsole.log(instance._protectedProperty) // \"yoohoo\"\n```\n\nThe good news is, you can use lowclass to add Protected and Private\nfunctionality to your existing classes!\n\nJust wrap your class with Class to gain Protected or Private functionality:\n\n```js\nimport {Class} from 'lowclass/dist/Class.js'\n\nconst Thing = Class(({Protected}) =\u003e {\n\treturn class Thing {\n\t\tconstructor() {\n\t\t\t// make the property truly protected\n\t\t\tProtected(this).protectedProperty = 'yoohoo'\n\t\t}\n\n\t\tsomeMethod() {\n\t\t\tconsole.log('Protected value is:', Protected(this).protectedProperty)\n\t\t}\n\t}\n})\n```\n\nWe can make it a little cleaner:\n\n```js\nconst Thing = Class(\n\t({Protected}) =\u003e\n\t\tclass {\n\t\t\tconstructor() {\n\t\t\t\tProtected(this).protectedProperty = 'yoohoo'\n\t\t\t}\n\n\t\t\tsomeMethod() {\n\t\t\t\treturn Protected(this).protectedProperty\n\t\t\t}\n\t\t},\n)\n```\n\nIf we were exporting this from a module, we could write it like this:\n\n```js\nexport default Class(\n\t({Protected}) =\u003e\n\t\tclass Thing {\n\t\t\tconstructor() {\n\t\t\t\tProtected(this).protectedProperty = 'yoohoo'\n\t\t\t}\n\n\t\t\tsomeMethod() {\n\t\t\t\treturn Protected(this).protectedProperty\n\t\t\t}\n\t\t},\n)\n```\n\nYou might still be making ES5-style classes using `function() {}` instead of\n`class`. In this case wrapping it would look like this:\n\n```js\nconst Thing = Class(({Protected}) =\u003e {\n\tfunction Thing() {\n\t\tProtected(this).protectedProperty = 'yoohoo'\n\t}\n\n\tThing.prototype = {\n\t\tconstructor: Thing,\n\n\t\tsomeMethod() {\n\t\t\treturn Protected(this).protectedProperty\n\t\t},\n\t}\n\n\treturn Thing\n})\n```\n\nAnd it works:\n\n```js\nconst t = new Thing()\n\nexpect(t.someMethod()).toBe('yoohoo')\n\n// the value is not publicly accessible!\nexpect(t.protectedProperty).toBe(undefined)\n```\n\nBut this is a fairly simple example. Let's show how inheritance of protected\nmembers works, again wrapping a native ES6+ `class`. Suppose we have a derived\nclass that is also using the not-actually-protected underscore convention:\n\n```js\nclass Something extends Thing {\n\totherMethod() {\n\t\t// we'll need to update this\n\t\treturn this._protectedProperty\n\t}\n}\n```\n\nWe will wrap it with lowclass too, so that it can inherit the protected member:\n\n```js\nconst Something = Class(\n\t({Protected}) =\u003e\n\t\tclass extends Thing {\n\t\t\totherMethod() {\n\t\t\t\t// access the inherited actually-protected member\n\t\t\t\treturn Protected(this).protectedProperty\n\t\t\t}\n\t\t},\n)\n```\n\nIf you are writing ES5-style classes, it will look something like this:\n\n```js\nconst Something = Class(({Protected}) =\u003e {\n\tfunction Something() {\n\t\tThing.call(this)\n\t}\n\n\tSomething.prototype = {\n\t\t__proto__: Thing.prototype,\n\t\tconstructor: Something,\n\n\t\totherMethod() {\n\t\t\t// access the inherited actually-protected member\n\t\t\treturn Protected(this).protectedProperty\n\t\t},\n\t}\n\n\treturn Something\n})\n```\n\nAnd it works:\n\n```js\nconst s = new Something()\nexpect(s.protectedProperty).toBe(undefined)\nexpect(s.otherMethod()).toBe('yoohoo')\n```\n\nNice, we can keep internal implementation hidden, and prevent people from using\nour APIs in unexpected ways!\n\n### Private members\n\nContinuing from above, if we use a Private member instead of a Protected member\nin a derived subclass, the subclass will not be able to access the private\nmember of the parent class (like C++ and Java).\n\nHere's an example that shows the concept, but this time we will define the\nclasses directly with lowclass, instead of wrapping a class:\n\n```js\nimport {Class} from 'lowclass/dist/Class.js'\n\nconst Thing = Class(({Private}) =\u003e ({\n\tconstructor() {\n\t\tPrivate(this).privateProperty = 'yoohoo'\n\t},\n}))\n\nconst Something = Thing.subclass(({Private}) =\u003e ({\n\totherMethod() {\n\t\treturn Private(this).privateProperty\n\t},\n}))\n\nconst something = new Something()\n\n// the private member can't be accessed by the subclass code:\nexpect(something.otherMethod()).toBe(undefined)\n```\n\nAs you can see, code in the child class (`otherMethod`) is unable to access the\nprivate value of the parent class.\n\n### Private Inheritance\n\nIn the last example, We've learned that, like in C++ or Java, subclasses can\nnot access parent class private members.\n\nBut lowclass offers something that C++ and Java do not: Private Inheritance.\nSubclasses can inherit (make use of) private functionality from a parent class.\nA subclass can call an inherited private method, but the interesting thing is\nthat the inherited private method _will operate on the private data of the\nsubclass, not of the parent class_.\n\nLet's illustrate this with an example, then we'll explain afterwords how it\nworks:\n\n```js\nimport {Class} from 'lowclass/dist/Class.js'\n\nconst Thing = Class(({Private}) =\u003e ({\n\tconstructor() {\n\t\tPrivate(this).privateProperty = 'yoohoo'\n\t},\n\n\tsomeMethod() {\n\t\treturn Private(this).privateProperty\n\t},\n\n\tchangeIt() {\n\t\tPrivate(this).privateProperty = 'oh yeah'\n\t},\n}))\n\nconst Something = Class().extends(Thing, ({Private}) =\u003e ({\n\totherMethod() {\n\t\treturn Private(this).privateProperty\n\t},\n\n\tmakeItSo() {\n\t\tPrivate(this).privateProperty = 'it is so'\n\t},\n}))\n\nconst instance = new Something()\n\nexpect(instance.someMethod()).toBe('yoohoo')\nexpect(instance.otherMethod()).toBe(undefined)\n\ninstance.changeIt()\nexpect(instance.someMethod()).toBe('oh yeah')\nexpect(instance.otherMethod()).toBe(undefined)\n\ninstance.makeItSo()\nexpect(instance.someMethod()).toBe('oh yeah')\nexpect(instance.otherMethod()).toBe('it is so')\n```\n\n\u003e Huh? What?\n\nIn every class hierarchy, there is a private scope for each class in the\nhierarchy (just like in C++ and Java). In this case, there's two private\nscopes: one for `Thing`, and one for `Something`. `Thing.someMethod` and\n`Thing.changeIt` are accessing the `privateProperty` of `Thing`, while\n`Something.otherMethod` and `Something.makeItSo` are accessing the\n`privateProperty` of `Something`.\n\nBut unlike C++ and Java, lowclass has a concept of private inheritance, where a\nsubclass can re-use private logic of a parent class, but the logic will operate\non private members of the class scope where it is used.\n\nTo use inheritable functionality, all that you have to do is run private code\nin the code of a subclass. Let's make one more example to show what this means\nin another way:\n\n```js\nconst Counter = Class(({Private}) =\u003e ({\n\tprivate: {\n\t\t// this is a prototype property, the initial private value will be\n\t\t// inherited by subclasses\n\t\tcount: 0,\n\n\t\tincrement() {\n\t\t\tthis.count++\n\t\t},\n\t},\n\n\ttick() {\n\t\tPrivate(this).increment()\n\n\t\treturn Private(this).count\n\t},\n\n\tgetCountValue() {\n\t\treturn Private(this).count\n\t},\n}))\n\nconst DoubleCounter = Counter.subclass(({Private}) =\u003e ({\n\tdoubleTick() {\n\t\t// to use inherited private functionality in a subclass, simply use\n\t\t// the functionality in the code of the subclass.\n\t\tPrivate(this).increment()\n\t\tPrivate(this).increment()\n\n\t\treturn Private(this).count\n\t},\n\n\tgetDoubleCountValue() {\n\t\treturn Private(this).count\n\t},\n}))\n\nconst counter = new Counter()\n\nexpect(counter.tick()).toBe(1)\n\nconst doubleCounter = new DoubleCounter()\n\nexpect(doubleCounter.doubleTick()).toBe(2)\nexpect(doubleCounter.tick()).toBe(1)\n\nexpect(doubleCounter.doubleTick()).toBe(4)\nexpect(doubleCounter.tick()).toBe(2)\n\n// There's a private `counter` member for the Counter class, and there's a\n// separate private `counter` member for the `DoubleCounter` class (the\n// initial value inherited from `Counter`):\nexpect(doubleCounter.getDoubleCountValue()).not.toBe(counter.getCountValue())\nexpect(doubleCounter.getCountValue()).toBe(2)\nexpect(doubleCounter.getDoubleCountValue()).toBe(4)\n```\n\nThe inherited private functionality has to be triggered directly, as triggering\nit indirectly will make it behave like in C++ and Java. This is why when we\ncalled `doubleCounter.tick()` the private functionality operated on the private\n`count` property of the `Counter` class, not the `DoubleCounter` class.\n\nThe key thing to learn from this is that when private code is used, it operates\non the class scope where the code is triggered. In the case of `DoubleCounter`,\nwe trigger the inherited functionality inside of the `DoubleCounter.doubleTick`\nmethod, so this makes the inherited functionality operate on `DoubleCounter`'s\ninherited private `count` property.\n\n### \"friends\" like in C++, or \"package protected\" like in Java\n\nLowclass makes it possible to do something similar to \"friend\" in C++ or\n\"package protected\" in Java. We can do these sorts of things by \"leaking\" the\naccess helpers to a scope that is outside a class definition.\n\nFor example, in the following example, the `Counter` class has private data,\nand the `Incrementor` class can access the protected member of the `Counter`\nclass although `Incrementor` is not derived from `Counter`. These two classes\nare exported and then imported by another file which can not access the private\ndata, but can use the public API of both classes to make instances of the two\nclasses interact with eachother.\n\n```js\n// Counter.js\n\n// show how to do something similar to \"friend\" in C++ or \"package protected\"\n// in Java.\n\nimport {Class} from 'lowclass/dist/Class.js'\n\nlet CounterProtected\n\nconst Counter = Class(({Private, Protected}) =\u003e {\n\t// leak the Counter class Protected helper to outer scope\n\tCounterProtected = Protected\n\n\treturn {\n\t\tvalue() {\n\t\t\treturn Private(this).count\n\t\t},\n\n\t\tprivate: {\n\t\t\tcount: 0,\n\t\t},\n\n\t\tprotected: {\n\t\t\tincrement() {\n\t\t\t\tPrivate(this).count++\n\t\t\t},\n\t\t},\n\t}\n})\n\n// note how Incrementor does not extend from Counter\nconst Incrementor = Class(({Private}) =\u003e ({\n\tconstructor(counter) {\n\t\tPrivate(this).counter = counter\n\t},\n\n\tincrement() {\n\t\tconst counter = Private(this).counter\n\t\tCounterProtected(counter).increment()\n\t},\n}))\n\nexport {Counter, Incrementor}\n```\n\n```js\n// shows that functionality similar to \"friend\" in C++ or \"package\n// protected\" can be done with lowclass. See `./Counter.js` to learn how it\n// works.\n\nimport {Counter, Incrementor} from './Counter'\n\n// in a real-world scenario, counter might be used here locally...\nconst counter = new Counter()\n\n// ...while incrementor might be passed to third party code.\nconst incrementor = new Incrementor(counter)\n\n// show that we can only access what is public\nexpect(counter.count).toBe(undefined)\nexpect(counter.increment).toBe(undefined)\nexpect(typeof counter.value).toBe('function')\n\nexpect(incrementor.counter).toBe(undefined)\nexpect(typeof incrementor.increment).toBe('function')\n\n// show that it works:\nexpect(counter.value()).toBe(0)\nincrementor.increment()\nexpect(counter.value()).toBe(1)\nincrementor.increment()\nexpect(counter.value()).toBe(2)\n```\n\n## Forms of writing classes\n\nWorking examples of the various forms depicted here are in\n[`tests/syntaxes.test.js`](./tests/syntaxes.test.js).\n\n### Simple object literals\n\nIf we will only use public members in our class, we can define a class with a\nsimple object literal in a few ways.\n\nHere's a named class, and in this case it is a little redundant as there are\ntwo occurrences of \"Thing\" in the definition:\n\n```js\nconst Thing = Class( 'Thing', {\n    method() { ... }\n})\n```\n\nAn anonymous class can avoid redundancy, and new engines are good at showing\nyou variable names in the console when classes or functions are anonymous:\n\n```js\nconst Thing = Class({\n    method() { ... }\n})\n```\n\nA named class can be useful for debugging in older environments, and when used\nwith with direct exports as there's no redundancy:\n\n```js\nexport default Class( 'Thing', {\n    method() { ... }\n})\n```\n\nIf you're not using Protected or Private members, you probably don't need to\neven use lowclass, and native `class` syntax can give you all the Public\nfunctionality that you need.\n\n### Definer functions give us access to access helpers.\n\nThere's also a [proposal for private\nmembers](https://github.com/tc39/proposal-class-fields) in the works, but who\nknows how long until it makes its way into engines, if ever.\n\nUntil then, we can use a \"definer function\" when defining a class with\nlowclass, so that we can access Public, Protected, Private, and Super helpers.\n\nInstead of providing a simple object literal as above, we can provide a\nfunction that receives access helpers. This function should then return the\nobject literal that contains the definition of the class, or should return a\ncustom-made class constructor.\n\n#### Returning an object literal\n\n```js\nexport default Class('Thing', function (Public, Protected, Private, Super) {\n\treturn {\n\t\tmethod() {\n\t\t\t// use any of the helpers inside the class code, as needed, f.e.\n\n\t\t\t// access Public members\n\t\t\tthis.foo = 'foo'\n\n\t\t\t// access Protected members\n\t\t\tProtected(this).bar = 'bar'\n\n\t\t\t// access Private members\n\t\t\tPrivate(this).baz = 'baz'\n\t\t},\n\t}\n})\n```\n\nTo make code shorter, you can combine arrow functions with destructuring of\narguments. In this exampe, we only need the Private helper:\n\n```js\nexport default Class('Thing', ({Private}) =\u003e ({\n\tmethod() {\n\t\t// access Private members\n\t\tPrivate(this).baz = 'baz'\n\t},\n}))\n```\n\n#### Returning a class constructor\n\nIf you want to make your classes in your own way, you can return a class from a\ndefiner function, which is useful for wrapping existing classes in order to\ngive them protected and private functionality:\n\n```js\nexport default Class(({Private}) =\u003e {\n\treturn class {\n\t\tmethod() {\n\t\t\tPrivate(this).baz = 'baz'\n\t\t}\n\t}\n})\n\n// or\n\nexport default Class(\n\t({Private}) =\u003e\n\t\tclass {\n\t\t\tmethod() {\n\t\t\t\tPrivate(this).baz = 'baz'\n\t\t\t}\n\t\t},\n)\n```\n\n### ES5-like assignment to prototype\n\nYou might have lots of ES5-style code, so this form can be useful in porting\nover to lowclass more quickly, or maybe you just like this form more.\n\n```js\nexport default Class('Thing', ({Public, Private}) =\u003e {\n\tPublic.prototype.method = function () {\n\t\tPrivate(this).baz = 'baz'\n\t}\n})\n```\n\n### Subclasses\n\nWe can make a subclass in a couple ways, with ot without names, and using\nobject literals or definer functions. We'll use the `Super` helper to access\nsuper methods.\n\n#### With `.extends`\n\nThis way is more similar to native classes:\n\n```js\nconst Something = Class().extends(Thing, ({Super}) =\u003e ({\n\tmethod() {\n\t\tSuper(this).method()\n\t},\n}))\n```\n\nAnd as before, naming the class can be useful:\n\n```js\nexport default Class('Something').extends(Thing, ({Private}) =\u003e ({\n\tmethod() {\n\t\tSuper(this).method()\n\t},\n}))\n```\n\n#### With `.subclass`\n\nHere's same subclass example using `.subclass`:\n\n```js\nconst Something = Thing.subclass(({Super}) =\u003e ({\n\tmethod() {\n\t\tSuper(this).method()\n\t},\n}))\n```\n\nAnd as before, naming the class can be useful:\n\n```js\nexport default Thing.subclass('Something', ({Super}) =\u003e ({\n\tmethod() {\n\t\tSuper(this).method()\n\t},\n}))\n```\n\nWe can also stick lowclass onto any constructor, and use it just like the\nprevious example:\n\n```js\nimport {Class} from 'lowclass/dist/Class.js'\n\nArray.subclass = Class\n\nconst MyArray = Array.subclass( ({ Super, Private }) =\u003e {\n    constructor() {\n        const self = super.constructor(...args)\n        self.__proto__ = MyArray.prototype\n\n        Private(self).message = 'I am Array!'\n\n        return self\n    },\n})\n```\n\nSee the full Array example in\n[`test/extending-builtins.test.js`](./test/extending-builtins.test.js).\n\n## Differences between lowclass and other languages\n\n### C++\n\nC++ and lowclass are basically the same (including \"friend\" classes). Where\nthey differ is that lowclass offers \"Private Inheritance\" as described above\nwhile C++ does not.\n\nSee [here](https://www.geeksforgeeks.org/access-modifiers-in-c/) for an\nexplainer on C++ access modifers which is effectively the same for lowclass.\n\n### Java\n\nThe differences between lowclass' and Java's access modifiers are basically the\nsame as the differences between C++ and Java. Lowclass additionally has\n\"Private Inheritance\". Lowclass also has a concept similar to \"package\nprotected\" which is similar to \"friend\" in C++.\n\nSee [here](https://www.javatpoint.com/access-modifiers) for an explainer of\nJava access modifiers. We can compare this against C++, and therefore also\nagainst lowclass.\n\n## TODO\n\n- [ ] public/protected/private/super helpers for static members\n- [ ] ability to make classes \"final\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrusktr%2Flowclass","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrusktr%2Flowclass","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrusktr%2Flowclass/lists"}