{"id":19649115,"url":"https://github.com/toxic-johann/toxic-decorators","last_synced_at":"2025-09-03T13:38:40.438Z","repository":{"id":57378441,"uuid":"94184253","full_name":"toxic-johann/toxic-decorators","owner":"toxic-johann","description":"Library of Javascript decorators","archived":false,"fork":false,"pushed_at":"2022-10-09T04:59:40.000Z","size":945,"stargazers_count":30,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-18T17:07:05.028Z","etag":null,"topics":["accessor","autobind","class","decorators","ecmascript","javascript","javascript-decorators","lock","proxy","stage-0","toxic-decorators","utils","waituntil","watch","watcher"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/toxic-johann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"toxic-johann"}},"created_at":"2017-06-13T07:35:11.000Z","updated_at":"2025-03-22T07:55:37.000Z","dependencies_parsed_at":"2023-01-19T18:32:38.018Z","dependency_job_id":null,"html_url":"https://github.com/toxic-johann/toxic-decorators","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toxic-johann%2Ftoxic-decorators","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toxic-johann%2Ftoxic-decorators/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toxic-johann%2Ftoxic-decorators/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toxic-johann%2Ftoxic-decorators/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toxic-johann","download_url":"https://codeload.github.com/toxic-johann/toxic-decorators/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251345743,"owners_count":21574766,"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":["accessor","autobind","class","decorators","ecmascript","javascript","javascript-decorators","lock","proxy","stage-0","toxic-decorators","utils","waituntil","watch","watcher"],"created_at":"2024-11-11T14:51:38.877Z","updated_at":"2025-04-28T16:30:33.546Z","avatar_url":"https://github.com/toxic-johann.png","language":"JavaScript","funding_links":["https://github.com/sponsors/toxic-johann"],"categories":[],"sub_categories":[],"readme":"# toxic-decorators\n\n[![Build Status](https://img.shields.io/travis/toxic-johann/toxic-decorators/master.svg?style=flat-square)](https://travis-ci.org/toxic-johann/toxic-decorators.svg?branch=master)\n[![Coverage Status](https://img.shields.io/coveralls/toxic-johann/toxic-decorators/master.svg?style=flat-square)](https://coveralls.io/github/toxic-johann/toxic-decorators?branch=master)\n[![npm](https://img.shields.io/npm/v/toxic-decorators.svg?colorB=brightgreen\u0026style=flat-square)](https://www.npmjs.com/package/toxic-decorators)\n[![npm download times](https://img.shields.io/npm/dm/toxic-decorators.svg)](https://www.npmjs.com/package/toxic-decorators)\n[![dependency Status](https://david-dm.org/toxic-johann/toxic-decorators.svg)](https://david-dm.org/toxic-johann/toxic-decorators)\n[![devDependency Status](https://david-dm.org/toxic-johann/toxic-decorators/dev-status.svg)](https://david-dm.org/toxic-johann/toxic-decorators?type=dev) [![Greenkeeper badge](https://badges.greenkeeper.io/toxic-johann/toxic-decorators.svg)](https://greenkeeper.io/)\n\n\n\n\u003e Inspired by [core-decorators written by jayphelps](https://github.com/jayphelps/core-decorators.js). I think decorators will one powerful util for developer. So I create some function I want to use.\n\nLibrary of [JavaScript stage-0 decorators](https://github.com/wycats/javascript-decorators) (aka ES2016/ES7 decorators [but that's not accurate](https://medium.com/@jayphelps/please-stop-referring-to-proposed-javascript-features-as-es7-cad29f9dcc4b)) include methods like @autobind, @waituntil, @alias etc. It's mainly focus on some useful methods to help us create javascript application.\n\nIf you have any idea of function you want. Please tell me or help me.\n\n## state of decoratos\n\n\u003eMost of the paragraph below  is mainly quoted from  [core-decorators written by jayhelps](https://github.com/jayphelps/core-decorators.js).\n\u003e\n\u003eThese are stage-0 decorators because while [the decorators spec has changed](http://tc39.github.io/proposal-decorators/) and is now stage-2, no transpiler has yet to implement these changes and until they do, this library won't either. Although the [TypeScript documentation](http://www.typescriptlang.org/docs/handbook/decorators.html)uses the phrase \"Decorators are a stage 2 proposal for JavaScript\" this is misleading because TypeScript still only implements the **stage-0** version of the spec, which is very incompatible with stage-2 (as of this writing). Though we have [babel-plugin-transform-decorators-stage-2-initial](https://github.com/jkrems/babel-plugin-transform-decorators-stage-2-initial) to translate stage-2 version of decorators. But the author do not encourage us to use it.\n\u003e\n\u003eSo I think we will support stage-2 when we have mature compiler.\n\n## Get start\n\n**npm**\n\nA version compiled to ES5 in CJS format is published to npm as [toxic-decorators](https://www.npmjs.com/package/toxic-decorators).\n\nIf you want to use it in Node.js.\n\n```sh\nnpm install --save toxic-decorators\n```\n\nIf you want to use it in the front-end project, I encourage you to use:\n\n```sh\nnpm install --save-dev toxic-decorators\n```\n\n**just get the code**\n\nYou can get the compiled code in the `lib` file\n\n* `lib/toxic-decorators.js`  cjs version, require babel-runtime\n* `lib/toxic-decorators.mjs`  es version, which face to js:next require babel-runtime\n* `lib/toxic-decorators.browser.js`  umd version, which you can use in the browser, but maybe you will need to add babel-polyfill in some situation.\n* `lib/toxic-decorators.min.js` minify version based on umd version.\n\n## Decorators\n\n**For Properties and Methods**\n\n* [@accessor](#accessor)\n* [@alias](#alias)\n* [@enumerable](#enumerable)\n* [@initialize](#initialize)\n* [@nonconfigurable](#nonconfigurable)\n* [@configurable](#configurable)\n* [@readonly](#readonly)\n* [@frozen](#frozen)\n* [@lock](#lock)\n\n**For Properties**\n\n* [@initString](#initstring)\n* [@initNumber](#initnumber)\n* [@initArray](#initarray)\n* [@initBoolean](#initboolean)\n* [@alwaysString](#alwaysstring)\n* [@alwaysNumber](#alwaysnumber)\n* [@alwaysArray](#alwaysarray)\n* [@alwaysBoolean](#alwaysboolean)\n* [@lazyInit](#lazyinit)\n* [@nonextendable](#nonextendable)\n* [@watch](#watch)\n\n**For Methods**\n\n* [@autobind](#autobind)\n* [@before](#before)\n* [@after](#after)\n* [@waituntil](#waituntil)\n* [@runnable](#runnable)\n\n**For Classes**\n\n* [@autobind](#autobind)\n* [@autobindClass](#autobindclass)\n* [@beforeClass](#beforeClass)\n* [@afterClass](#afterClass)\n* [@waituntilClass](#waituntilClass)\n* [@runnableClass](#runnableClass)\n\n## Helpers\n\n* [applyDecorators()](#applydecorators)\n* [classify()](#classify)\n\n## Utils\n\n* [getOwnKeys](#getOwnKeys)\n* [getOwnPropertyDescriptors](#getOwnPropertyDescriptors)\n\n## Docs\n\n### @accessor\n\nSet getter/setter hook on any properties or methods. In fact, it will change all kind of descriptors into an accessor descriptor.\n\n**arguments**\n\n* **handler**: `Object`\n  * get: `Function | Array\u003cFunction\u003e`\n  * set: `Function | Array\u003cFunction\u003e`\n* **option**: `Object`\n  * preSet: `boolean`\n  * preGet: `boolean`\n\n```javascript\nimport {accessor, applyDecorators} from 'toxic-decorators';\n\nclass Foo {\n  bar = 1;\n  constructor () {\n    applyDecorators(this, {\n      bar: accessor({\n        get (value) {\n          // must return value here\n          return ++value;\n        },\n        set (value) {\n          return ++value\n        }\n      })\n    }, {self: true});\n  }\n}\n\nconsole.log(foo.bar); // 2\nfoo.bar = 3;\nconsole.log(foo.bar); // 5\n```\n\n\u003e The example may be werid. You may wonder [why we can not use @accessor on InitializeInstanceFields directy?](#why-we-can-not-use-accessor-on-initializeinstancefields-directy)\n\n### @alias\n\nHelp you to set alias for properties  on any instance or for methods on any class.\n\n**arguments**\n\n* **other**: `non-primitive` *optional* the other instance you want set alias on\n* **name**: `string` the alias name\n* options: `object` *optional*\n  * force: `boolean`\n    * when it's true, we will redifine the exiting property, otherwise, we will throw an error when we find you are setting alias on existing property\n    * But it's impossible to do something on frozen value.\n  * omit: `boolean`\n    * when it's true, we will just skip the existing property.\n\n```javascript\nimport {alias, applyDecorators} from 'toxic-decorators';\n\nclass Cat {};\nconst cat = new Cat();\nclass Dog {\n  @alias('run')\n  @alias('run', Cat.prototype)\n  @alias('move', cat)\n  move() {\n    console.log('it moved');\n  }\n  age = 1;\n  construcotr () {\n    applyDecorators(this, {\n      age: [alias('old'), alias('age', cat)]\n    }, {self: true})\n  }\n}\nconst dog = new Dog();\nconst antoherCat = new Cat();\ndog.move(); // it moved\ndog.run(); // it moved\ncat.run(); // it moved\nanotherCat.run(); // it moved\ncat.move(); // it moved\nconsole.log(anotherCat.move === undefined); // true\nconsole.log(cat.age); // 1\nconsole.log(dog.old); // 1\n```\n\n\u003e You can also set alias on getter/setter too.\n\u003e\n\u003e But there's one problem is we will set the alias until the origin one has been initialized.\n\u003e\n\u003e It means that you must get access to your origin property before you get access to your alias property, otherwise the alias one will be undefined.\n\u003e\n\u003e You may wonder [why we can not use @accessor on InitializeInstanceFields directy?](#why-we-can-not-use-accessor-on-initializeinstancefields-directy)\n\n### @configurable\n\nSet a property's configurable to be `true`. \n\n\u003e  You can know more why I bump into this problem by [why configurable of InitializeInstanceFields is false when I use decorators on it?](#why-configurable-of-initializeinstancefields-is-false-when-i-use-decorators-on-it)\n\n**arguments** none.\n\n```javascript\nimport {configurable, initString} from 'toxic-decorators';\n\nclass Foo {\n  @configurable\n  @initString()\n  bar = '123';\n}\ndelete foo.bar;\n```\n\n### @nonconfigurable\n\nMakes a porperty or method so that they cannot be deleted. Also accroding to the specification, it can prevent them from editing via `Object.defineProperty`. But it doesn't work quiet well. In that situation,  [@readonly](#readonly) may be a better choice.\n\n**arguments** none.\n\n```javascript\nimport {nonconfigurable} from 'toxic-decorators';\n\nclass Foo {\n  @nonconfigurable\n  bar = 1;\n}\ndelete foo.bar; // Cannot delete property 'bar' of #\u003cFoo\u003e\"\n```\n\n### @enumerable\n\nMarks a property or method as being enumerable. As we know, property is enumerable by default.\n\n**arguments** none.\n\n```javascript\nimport {enumerable} from 'toxic-decoarators';\n\nclass Foo {\n  @enumerable\n  bar () {}\n  car () {}\n}\n\nconst foo = new Foo();\nfor (const key in foo) console.log(key);\n// bar\n```\n\n### @nonenumerable\n\nMarks a property as not being enumerable. Note that methods aren't enumerable by default.\n\n**arguments** none.\n\n```javascript\nimport {nonenumerable} from 'toxic-decorators';\n\nclass Foo {\n  @nonenumerable\n  a = 1;\n  b = 2;\n}\n\nconst foo = new Foo();\nfor (const key in foo) console.log(key); // b\n```\n\n### @initialize\n\nHelp you to do something when you initialize your property or function.\n\n**arguments** \n\n* **fn1** `Function` the handler\n* **fn2** `Function` the handler\n* … and so on\n\n```javascript\nimport {initialize} from 'toxic-decorators';\n\nclass Foo {\n  @initialize(function (value) {\n    return ++value;\n  })\n  bar = 1;\n};\nconst foo = new Foo();\nconsole.log(foo.bar); // 2;\nfoo.bar = 3;\nconsole.log(foo.bar); // 3\n```\n\n\u003e You can use this on getter/setter, too. Once you use that, we will always run the initialze function that until you set the value again.\n\n### @readonly\n\nYou cannot write the porperty again.\n\n**arguments** none\n\n```javascript\nimport { readonly } from 'toxic-decorators';\n\nclass Meal {\n  @readonly\n  entree = 'steak';\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot assign to read only property 'entree' of [object Object]\n```\n\nYou can also use readonly on getter/setter, but there is something you should pay attention.\n\nWe have just remove the setter here. But you getter stillreturn the origin value. You can change the origin value.\n\n```javascript\nimport { readonly } from 'toxic-decorators';\n\nlet dish = 'steak'\n\nclass Meal {\n  @readonly\n  get entree () {return dish};\n  set entree (value) {\n    dish = value;\n    return dish\n  }\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot set property dinner of #\u003cMeal\u003e which has only a getter\ndish = 'salmon';\nconsole.log(dinner.entree); // 'salmon'\n```\n\n### @frozen\n\nWe will totally freeze the property. It can not be rewrite, delete or iterate.\n\n**arguments** none\n\n```javascript\nimport { frozen } from 'toxic-decorators';\n\nclass Meal {\n  @frozen\n  entree = 'steak';\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot assign to read only property 'entree' of [object Object]\ndelete dinner.entree;\n// Cannot delete property 'entree' of #\u003cMeal\u003e\"\n```\n\nYou can also set the getter/setter property frozen. In this way, it's value could change once it's settle down.\n\n```javascript\nimport { frozen } from 'toxic-decorators';\n\nlet dish = 'steak'\n\nclass Meal {\n  @frozen\n  get entree () {return dish};\n  set entree (value) {\n    dish = value;\n    return dish\n  }\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot set property dinner of #\u003cMeal\u003e which has only a getter\ndish = 'salmon';\nconsole.log(dinner.entree); // 'steak'\n```\n\n\u003e Note: Escpecially on property, Once you set frozen, it can't be change, even with decorators. So you may better put it on the top.\n\n### @lock\n\nWe will totally lock the property. It can not be rewrite, delete. But we would not force it be nonenumerable.\n\n**arguments** none\n\n```javascript\nimport { lock } from 'toxic-decorators';\n\nclass Meal {\n  @lock\n  entree = 'steak';\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot assign to read only property 'entree' of [object Object]\ndelete dinner.entree;\n// Cannot delete property 'entree' of #\u003cMeal\u003e\"\n```\n\nYou can also set the getter/setter property locked. In this way, it's value could change once it's settle down.\n\n```javascript\nimport { frozen } from 'toxic-decorators';\n\nlet dish = 'steak'\n\nclass Meal {\n  @lock\n  get entree () {return dish};\n  set entree (value) {\n    dish = value;\n    return dish\n  }\n}\n\nconst dinner = new Meal();\ndinner.entree = 'salmon';\n// Cannot set property dinner of #\u003cMeal\u003e which has only a getter\ndish = 'salmon';\nconsole.log(dinner.entree); // 'steak'\n```\n\n\u003e Note: Escpecially on property, Once you set locked, it can't be change, even with decorators. So you may better put it on the top.\n\n### @initString\n\nEnsure a property's initial value must be string. You can also pass another function as you want. It's just a grammar sugar for [@initialize](#initialize).\n\n**arguments** \n\n* **defaultValue** *optional* set the default value when value is not string\n* **fn1** `Function` the handler\n* **fn2** `Function` the handler\n* … and so on\n\n```Javascript\nimport {initString} from 'toxic-decorators';\n\nconst info = {\n  name: 'Kobe Bryant',\n  champions: 5\n};\nclass Intro {\n  @initString(value =\u003e value.toLowerCase())\n  name = info.name\n  @initString(value =\u003e value.toLowerCase())\n  champions = info.champions\n}\nconst intro = new Intro();\nconsole.log(intro.name); // kobe bryant\nconsole.log(intro.champions); // ''\n```\n\n### @initNumber\n\nEnsure a property's initial value must be number. You can see the detial in [@intiString](#initString)\n\n### @initBoolean\n\nEnsure a property's initial value must be boolean. You can see the detial in [@intiString](#initString)\n\n### @initArray\n\nEnsure a property's initial value must be Array. You can see the detial in [@intiString](#initString).\n\n### @alwaysString\n\nEnsure the property's value always be string. We change the property into getter/setter to implement this. It's a grammar sugar for [@accessor](#accessor).\n\n**arguments** \n\n* **defaultValue** *optional* set the default value for situation that value is not string\n* **fn1** `Function` the handler\n* **fn2** `Function` the handler\n* … and so on\n\n```Javascript\nimport {alwaysString, applyDecorators} from 'toxic-decorators';\n\nclass Intro {\n  name = 'BEN';\n  constructor () {\n    applyDecorators(this, {\n      name: alwaysString(value =\u003e value.toLowerCase())\n    }, {self: true});\n  }\n}\nconst intro = new Intro();\nconsole.log(intro.name); // ben\nintro.name = 'JONES';\nconsole.log(intro.name); // jones\n```\n\n\u003e You may wonder [why we can not use @accessor on InitializeInstanceFields directy?](#why-we-can-not-use-accessor-on-initializeinstancefields-directy)\n\n### @alwaysNumber\n\nEnsure the property's value always be number. You can see the detail in [@alwaysString](#alwaysString)\n\n### @alwaysBoolean\n\nEnsure the property's value always be boolean. You can see the detail in [@alwaysString](#alwaysString)\n\n### @alwaysArray\n\nEnsure the property's value always be Array. You can see the detail in [@alwaysString](#alwaysString)\n\n### @lazyInit\n\nPrevents a property initializer from running until the decorated property is actually looked up. Useful to prevent excess allocations that might otherwise not be used, but be careful not to over-optimize things.\n\n**arguments** none.\n\n```javascript\nimport { lazyInit } from 'toxic-decorators';\n\nfunction createHugeBuffer() {\n  console.log('huge buffer created');\n  return new Array(1000000);\n}\n\nclass Editor {\n  @lazyInit\n  hugeBuffer = createHugeBuffer();\n}\n\nvar editor = new Editor();\n// createHugeBuffer() has not been called yet\n\neditor.hugeBuffer;\n// logs 'huge buffer created', now it has been called\n\neditor.hugeBuffer;\n// already initialized and equals our buffer, so\n// createHugeBuffer() is not called again\n```\n\n### @nonextendable\n\nTo make the object property could not be extend.\n\n```javascript\nimport { nonextendable} from 'toxic-decorators';\n\nclass Foo {\n  @nonextendable\n  bar = {\n    a: 1\n  }\n}\n\nconst foo = new Foo();\nfoo.bar.b = 2; // error!!\n```\n\n### @watch\n\nWatch a property. We will call the function you provide once we detect change on the value.\n\n**arguments**\n\n- **keyOrFn1** `string |Function` the string points to a function or just a function, it will be called once property is changed\n- **keyOrFn2** `string |Function` the string points to a function or just a function, it will be called once property is changed\n- … and so on\n- **option** `Object` optional\n  - **deep** `boolean`\n    - `true` we will call you method if we get change on content of object or array\n    - `false` we would not care about the change on content of object or array\n    - default is `false`\n  - **diff** `boolean`\n    - `true` we will only call your method if the new value is different from the old value\n    - `false` we will call the method once you set the property\n    - default is `true`\n  - **omit** `boolean`\n    - `true` we will omit some error in watch decorator\n    - `false` we will throw out the error\n    - default is `false`\n  - **proxy** `boolean`\n    - `true` we will use `Proxy` (if browser support) to spy on object and array. In this way,  you can set and delete property as you want. But you should be care about the proxy value, we will talk about that later. And proxy mode also support `__set` and `__del`.\n    - `false` we will use `Object.defineProperty` to spy on object and array. In this way, you should use `__set` or `__del` to set and delete property.\n    - default is `false`\n  - **other** non-primitive\n    - if you offer this, and you function is pass as string. We use the string to look up function on this instance\n  - **operationPrefix** `string`\n    - if you don't want to use `__set` and `__del` as method, you can change their prefix by using this property.\n\nNow we will show how to use `@watch`\n\n```javascript\nimport {watch, applyDecorators} from 'toxic-decorators';\nfunction fn (newVal, oldVal) {console.log(newVal, oldVal)}\nclass Foo {\n  bar = 1;\n  constructor () {\n    applyDecorators(this, {\n      bar: watch(fn)\n    }, {self: true});\n  }\n}\nconst foo = new Foo();\nfoo.bar = 2;// 2, 1\n```\n\n`@watch` can detect change on the content of object and array, if you set deep true\n\n```javascript\nimport {watch, applyDecorators} from 'toxic-decorators';\nfunction fn (newVal, oldVal) {console.log(newVal, oldVal)}\nclass Foo {\n  bar = [1, 2, 3];\n  baz = {\n    a: 1\n  };\n  constructor () {\n    applyDecorators(this, {\n      bar: watch(fn, {deep: true}),\n      baz: watch(fn, {deep: true})\n    }, {self: true});\n  }\n}\nconst foo = new Foo();\nfoo.bar.push(4); // [1, 2, 3, 4], [1, 2, 3, 4]\nfoo.baz.a = 2; // {a: 2}, {a: 2}\n```\n\nIf you're sure your environment support `Proxy`,  you can use proxy mode\n\n```javascript\nimport {watch, applyDecorators} from 'toxic-decorators';\nfunction fn (newVal, oldVal) {console.log(newVal, oldVal)}\nclass Foo {\n  baz = {\n    a: 1\n  };\n  constructor () {\n    applyDecorators(this, {\n      baz: watch(fn, {deep: true, proxy: true})\n    }, {self: true});\n  }\n}\nconst foo = new Foo();\nfoo.baz.b = 2; // {a: 1, b: 2}, {a: 1, b: 2}\ndelete foo.baz.b; // {a: 1}, {a: 1}\n```\n\nIf you're not sure you support `Proxy`, or you don't want to use proxy mode. You can change content with  `__set` and `__del`, which will also trigger the change method.\n\n```javascript\nimport {watch, applyDecorators} from 'toxic-decorators';\nfunction fn (newVal, oldVal) {console.log(newVal, oldVal)}\nclass Foo {\n  baz = {\n    a: 1\n  };\n  constructor () {\n    applyDecorators(this, {\n      baz: watch(fn, {deep: true, proxy: false})\n    }, {self: true});\n  }\n}\nconst foo = new Foo();\nfoo.baz.__set('b', 2); // {a: 1, b: 2}, {a: 1, b: 2}\nfoo.baz.__del('b'); // {a: 1}, {a: 1}\n```\n\n\u003e If you use proxy mode, you should pay attention on proxy value. As we know\n\u003e\n\u003e ```javascript\n\u003e const obj = {a: 1};\n\u003e console.log(obj === new Proxy(obj, {})); // false\n\u003e ```\n\u003e\n\u003e Once you set an object on the property watch by proxy, it is bind with proxy object. So if you set original object on it again, it will trigger the method.\n\u003e\n\u003e ```javascript\n\u003e import {watch, applyDecorators} from 'toxic-decorators';\n\u003e const obj = {a: 1};\n\u003e function fn (newVal, oldVal) {console.log('changed')}\n\u003e class Foo {\n\u003e   bar = obj;\n\u003e   baz = obj;\n\u003e   constructor () {\n\u003e     applyDecorators(this, {\n\u003e       bar: watch(fn, {deep: true}),\n\u003e       baz: watch(fn, {deep: true, proxy: true})\n\u003e     }, {self: true});\n\u003e   }\n\u003e }\n\u003e foo.bar = obj;\n\u003e foo.baz = obj; // changed\n\u003e ```\n\u003e You may wonder [why we can not use @accessor on InitializeInstanceFields directy?](#why-we-can-not-use-accessor-on-initializeinstancefields-directy)\n\u003e\n\n### @autobind\n\nForces invocation of this function to always have `this` refet to the class instance, even if the class  is passed around or would otherwise lose its `this`. e.g. `const fn = context.method`.\n\nYou can use it on the methods.\n\n**arguments** none.\n\n```javascript\nimport { autobind } from 'toxic-decorators';\n\nclass Person {\n  @autobind\n  getPerson() {\n  \treturn this;\n  }\n}\n\nconst person = new Person();\nconst { getPerson } = person;\n\ngetPerson() === person;\n// true\n```\n\n You can use it on entire class, it will bind all methods of the class.\n\n```Javascript\nimport { autobind } from 'toxic-decorators';\n\n@autobind\nclass Person {\n  getPerson() {\n    return this;\n  }\n\n  getPersonAgain() {\n    return this;\n  }\n}\n\nconst person = new Person();\nconst { getPerson, getPersonAgain } = person;\n\ngetPerson() === person;\n// true\n\ngetPersonAgain() === person;\n// true\n```\n\nWell, sometime we have lots of methods of class to bind, but not all of them. So we maybe need to exclude some of them. In this situation, you can use [@autobindClass](#autobindClass).\n\n### @before\n\nYou can add your preprocessor here on your methods.Mostly, we will use this to do some arguments check.\n\n**arguments** \n\n* **fn1** `Function` the handler\n* **fn2** `Function` the handler\n* … and so on\n\n```javascript\nimport {before} from 'toxic-decorators';\n\nclass Foo {\n  @before(function (a, b) {\n    if(typeof a !== 'number' || typeof b !== 'number') {\n      throw new Error('only accept number');\n    }\n    // return the arguments in array\n    return [a, b];\n  })\n  sum (a, b) {\n    return a + b;\n  }\n}\nconst foo = new Foo();\nfoo.sum(1, 3); // 4\nfoo.sum('1', 3); // only accept number\n```\n\n### @after\n\nYou can add your postprocessor here on your methods.\n\n**arguments** \n\n* **fn1** `Function` the handler\n* **fn2** `Function` the handler\n* … and so on\n\n```javascript\nimport {before} from 'toxic-decorators';\n\nclass Foo {\n  @after(function (ret) {\n    return ret + 1;\n  })\n  sum (a, b) {\n    return a + b;\n  }\n}\nconst foo = new Foo();\nfoo.sum(1, 3); // 5\n```\n\n### @runnable\n\nIn some situation, you may want your method could not be called. You can use [@waituntil](#waituntil) to implement this. But it may cost too much. So we offer you this method.\n\n**arguments**\n\n* **handler** `Function | string`\n  * `Function` will tell us can we call the function\n    * `return true;` we will call the method\n    * else we would not call the method\n  * `string`\n    * the string indicate the property's name. We will fetch the property\n    * if the property's value is `true`, we will call the method\n    * else we would not call it\n* **option**\n  - **other** `non-primitive`\n    - optional\n    - only useful when handler is `string`\n    - if it exist, we will look up the property on this instance\n    - else, we will look up on the class itself\n  - **backup** `Function`\n    - optional\n    - when backup is not a function, we will just skip the original method and do nothing\n    - if you provide a backup function, we will called it.\n    - It's useful if you want to throw out some error, when people call your method, but it's not runnable.\n\n```javascript\nimport {runnable} from 'toxic-decorators';\nclass Foo {\n  @runnable('b', {backup () {console.error('it is not runnable now');}})\n  a () {\n    console.log('i have been called');\n  }\n  b = false;\n}\n\nconst foo = new Foo();\nfoo.a(); // it is not runnable now\nfoo.b = true;\nfoo.a(); // i have been called\n```\n\n### @waituntil\n\nIn some situation, our application is not ready. But others can call our function. We hope that they can wait for us. This decorators can let your function do not run until the flag is true\n\n**arguments**\n\n* **handler** `Function | Promise\u003c*\u003e | string`\n  * `Function` will tell us can we call the function\n    * `return promise` , we will wait until resolved\n    * `return true`, we will run immediately\n    * `return false`, we would not run it.\n    * when you return promise, your function will become an **asynchronous** function.\n    * when you return false, your call will be throw away and **never run**.\n  * `Promise\u003c*\u003e`, we will wait until resolved.\n    *  your function will become an **asynchronous** function.\n  * `string` **recommend**\n    * we will get the property and spy on it according to the string.\n    * if the property do not equal to true when the function is called, we will put the function into waiting queue.\n    * once the property become true, we will run the function in the waiting queue\n    * if the property is true when the function is called, we will run the function immediately.\n* **option**\n  * **other** `non-primitive`\n    * optional\n    * only useful when handler is `string`\n    * if it exist, we will look up the property on this instance\n    * else, we will look up on the class itself\n\n```Javascript\nimport {waituntil} from 'toxic-decorators';\nlet promiseResolve;\nclass Bar {\n  flag = false;\n}\nconst bar = new Bar();\nclass Foo {\n  ready = new Promise(resolve =\u003e {promiseResolve = resolve});\n  @waituntil(function () {return this.ready})\n  runUntilPromise () {\n    console.log('Promise is resolve!');\n  }\n  @waituntil(function () {return bar.flag});\n  runUntilTrue () {\n    console.log('flag is true!');\n  }\n  @waituntil('flag', bar);\n  runUntilReady () {\n    console.log('bar.flag is true!');\n  }\n}\nconst foo = new Foo();\nfoo.runUntilPromise();\nfoo.runUntilTrue();\nfoo.runUntilReady();\nbar.flag = true;\n// bar.flag is true!\nfoo.runUntilTrue();\n// flag is true!\npromiseResolve();\nsetTimeout(async () =\u003e {\n  // Promise is resolve!\n  foo.runUntilPromise();\n  await foo.ready;\n  // Promise is resolve!\n}, 0)\n```\n\n### @autobindClass\n\nWhen you not pass options. [@autobindClass](#autobindClass) does totally the same as [@autobind](#autobind). It can decorate all the method of the class.\n\n[@autobindClass](#autobindClass) is created by [@classify](#classify), so it's arguments it's the same as the classifiedDecorator's arguments in [@classify](#classify).\n\n```Javascript\nimport {autobindClass} from 'toxic-decorators';\n\n@autobindClass({exclude: ['b']})\nclass Foo {\n  a () {\n    return this;\n  }\n  b () {\n    return this;\n  }\n}\n\nconst foo = new Foo();\nconst {a, b} = foo;\na() === foo; // true\nb() === foo; // false\n```\n\n### @beforeClass\n\n[@beforeClass](#beforeClass) is created by [@classify](#classify) and [@before](#before), so it's arguments it's the same as the classifiedDecorator's arguments in [@classify](#classify) and [@before](#before).\n\n```javascript\nimport {beforeClass} from 'toxic-decorators';\nimport {isFunction} from 'toxic-predicate-functions';\n\n@beforeClass({}, () =\u003e console.log('i am called before'))\nclass Foo {\n  a () {\n    console.log('i am a');\n  }\n  b () {\n    console.log('i am b');\n  }\n}\nconst foo = new Foo();\nfoo.a();\n// i am called before\n// i am a\nfoo.b();\n// i am caleed before\n// i am b\n```\n\n### @afterClass\n\n[@afterClass](#afterClass) is created by [@classify](#classify) and [@after](#after), so it's arguments it's the same as the classifiedDecorator's arguments in [@classify](#classify) and [@after](#after).\n\n```javascript\nimport {afterClass} from 'toxic-decorators';\nimport {isFunction} from 'toxic-predicate-functions';\n\n@afterClass({}, () =\u003e console.log('i am called after'))\nclass Foo {\n  a () {\n    console.log('i am a');\n  }\n  b () {\n    console.log('i am b');\n  }\n}\nconst foo = new Foo();\nfoo.a();\n// i am a\n// i am called after\nfoo.b();\n// i am b\n// i am caleed after\n```\n\n### runnableClass\n\n[@runnableClass](#runnableClass) is created by [@classify](#classify) and [@runnable](#runnable), so it's arguments it's the same as the classifiedDecorator's arguments in [@classify](#classify) and [@runnable](#runnable).\n\n```javascript\nimport {runnableClass} from 'toxic-decorators';\n@runnableClass({}, 'b', {backup () {console.error('it is not runnable now');}})\nclass Foo {\n  a () {\n    console.log('i have been called');\n  }\n  b = false;\n}\n\nconst foo = new Foo();\nfoo.a(); // it is not runnable now\nfoo.b = true;\nfoo.a(); // i have been called\n```\n\n### waituntilClass\n\n[@waituntilClass](#waituntilClass) is created by [@classify](#classify) and [@waituntil](#waituntil), so it's arguments it's the same as the classifiedDecorator's arguments in [@classify](#classify) and [@waituntil](#waituntil).\n\n```javascript\nimport {waituntilClass} from 'toxic-decorators';\nlet promiseResolve;\nclass Bar {\n  flag = false;\n}\nconst bar = new Bar();\n@waituntilClass({}, function () {return bar.flag});\nclass Foo {\n  runUntilTrue () {\n    console.log('flag is true!');\n  }\n}\nconst foo = new Foo();\nfoo.runUntilTrue();\nbar.flag = true;\nfoo.runUntilTrue();\n// flag is true!\n```\n\n### applyDecorators()\n\nIf you want to use decorators, you may need to use [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) to compile. What if you don't want to use that. You can use `applyDecorators`.\n\n**arguments**\n\n* **Class** the class you want to handle\n* **props** `{[string]: Function | Array\u003cFunction\u003e}` the props map and their handler\n* **option**\n  * **self** `boolean` \n    * `false` we will handle on the `Class.prototype`\n    * `true` we will handle on the Class itself\n    * default is false\n  * **omit** `boolean`\n    * If you want to apply decorators on unconfigurable property, it will throw error\n    * `false` to throw out the error\n    * `true` to omit the error\n    * default is false\n\n```javascript\nimport {applyDecorators, before} from 'toxic-decorators';\n\nclass Person {\n  run () {\n    console.log('i am running');\n  }\n  walk () {\n    console.log('i am walking');\n  }\n}\n\n// Besides class, you can also use normal function like `function foo {}`\napplyDecorators(Foo, {\n  // you can add only one function\n  walk: before(() =\u003e console.log('go')),\n  run: [before(() =\u003e console.log('ready')), before(() =\u003e console.log('go'))]\n});\n\nconst foo = new Foo();\nfoo.walk();\n// go\n// i am walking\nfoo.run();\n// ready\n// go\n// i am running\n```\n\nIn the way above, we can apply decorators on function's prototype. That's enough for methods. But what if we want to apply some property decorators.\n\nYou can act like above, but it will modify portotype's property. They make take effect on multiple instance, and it's works bad on some situation.\n\nSo, if you want to apply decorators on property, I advice you to pass in an instance in self mode.\n\n```javascript\nimport {initialize, applyDecorators} from 'toxic-decorators';\n\nclass Foo {\n  a = 1;\n  b = 2;\n};\nconst foo = new Foo();\nconsole.log(foo.a); // 1\nconsole.log(foo.b); // 2\n\napplyDecorators(foo, {\n  a: initialize(function () {return 2;}),\n  b: initialize(function () {return 3;})\n}, {self: true});\n\nconsole.log(foo.a); // 2\nconsole.log(foo.b); // 3\n```\n\nWhat's more, you can also use applyDecorators to decorate the whole class.\n\n**arguments**\n\n* **Class** the class you want us to handle\n* **decorators** `Function | Array\u003cFunction\u003e` handlers\n\n```Javascript\nimport {autobindClass, applyDecoratos} from 'toxic-decorators';\n\nclass Foo {\n  a () {\n    return this;\n  }\n  b () {\n    return this;\n  }\n}\n\napplyDecorators(Foo, autobindClass({exclude: ['b']}))\nconst foo = new Foo();\nconst {a, b} = foo;\na() === foo; // true\nb() === foo; // false\n```\n\n### classify()\n\nIf you want to decorate your class. You should add `@decorator` before your class. But what if you don't want to  [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) . You can use classify to create a function to decorate your class.\n\nWhat's more, adding `@decorator` before your class could only decorate the method. You may want to decorate the property too. In this situation, you may need to use the function created by `classify` with `self: true`.\n\n**arguments**\n\n* **decorator**\n  * `Function`\n  * the `decorator` you want to use\n* **option**\n  * **requirement**\n    * `Function`\n    * optional\n    * if you do not offer requirement, we will decorate all property and method\n    * if you offer us a requirement, we would not decorate the property and method if you return false\n  * **customeArgs**\n    * `boolean`\n    * default: `false`\n    * some decorator may support customArgs, you need to tell us that.\n\n**return**\n\n* **classifiedDecorator**\n  * `Function`\n  * the decorator which you can used on class\n  * **arguments**\n    * **option**\n      * **exclude**\n        * `Array\u003cstring\u003e`\n        * The name of property which you don't want to decorate\n      * **include**\n        * `Array\u003cstring\u003e`\n        * the name of property which is not exist now but you want to decorate on.\n      * **construct**\n        * `boolean`\n        * Default: `false`\n        * we will decorate the `constructor` if you set it  `true`\n      * **self**\n        * `boolean`\n        * Default: `false`\n        * when you want to decorate an instance, you should set `self` to be `true`, we will decorate the instance itself.\n    * other arguments will be pass into the decorator\n\n```javascript\nimport {before, classify} from 'toxic-decorators';\nimport {isFunction} from 'toxic-predicate-functions';\nconst beforeClass = classify(before, {\n  requirement (obj, prop, desc) {\n    return desc \u0026\u0026 isFunction(desc.value);\n  },\n  customArgs: true\n});\n\n@beforeClass({}, () =\u003e console.log('i am called before'))\nclass Foo {\n  a () {\n    console.log('i am a');\n  }\n  b () {\n    console.log('i am b');\n  }\n}\nconst foo = new Foo();\nfoo.a();\n// i am called before\n// i am a\nfoo.b();\n// i am caleed before\n// i am b\n```\n\n## Need lodash utilities as decorators?\n\n\u003e We have mostly the same idea with core-decorators. So I just quote this from it's README.\n\ntoxic-decorators aims to provide decorators that are fundamental to JavaScript itself--mostly things you could do with normal `Object.defineProperty` but not as easily when using ES2015 classes. Things like debouncing, throttling, and other more opinionated decorators are being phased out in favor of [lodash-decorators](https://www.npmjs.com/package/lodash-decorators) which wraps applicable lodash utilities as decorators. We don't want to duplicate the effort of lodash, which has years and years of robust testing and bugfixes.\n\n## Precautions\n\n### why configurable of InitializeInstanceFields is false when I use decorators on it?\n\n\u003e This bug is fixed in Babel 7\n\nWe all knows that, JavaScript class will support public fields later. But it bring use some problem.You can see in [this case](https://codepen.io/toxic-johann/pen/XgvGMy):\n\n```javascript\nfunction detect (obj, prop, descriptor) {\n  console.log(obj, prop, descriptor);\n  return descriptor;\n}\nclass Foo {\n  a = 1;\n  @detect\n  b = 2;\n}\nconst foo = new Foo(); // {configurable: false, enumerable: true, initializer: function initializer(), writable: true}\nconsole.log(Object.getOwnPropertyDescriptor(foo, 'a'), Object.getOwnPropertyDescriptor(foo, 'b'));\n// {configurable: true, enumerable: true, value: 1, writable: true}, {configurable: false, enumerable: true, value: 2, writable: true}\n\n```\n\nWell, according to the [specification](https://tc39.github.io/proposal-class-fields/#sec-define-field). The configurable of public field shoud be `false`. But this will make us could not use configure it later.\n\nIn this situation, you should use [@configurable](#configuralbe).\n\n### why we can not use @accessor on InitializeInstanceFields directy?\n\nDecorators like accessor will turn initialze descirptor into accessor descriptor. According to [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy/blob/master/src/index.js#L67-L72), it will bind accessor descriptor to class's prototype. In other words, it's singleton.\n\nThat may bring us problem, for example:\n\n```javascript\nclass Foo {\n  @accessor({\n    get (value) {\n      return value;\n    },\n    set (value) {\n      return value;\n    }\n  })\n  bar = 1;\n  baz = 2;\n}\nconst foo1 = new Foo();\nconst foo2 = new Foo();\nfoo2.bar = 3;\nconsole.log(foo1.bar, foo2.bar); // 3， 3\n```\n\nAs value are all set on the `Foo.prototype`, once you set the value. It will change both instance.\n\nHowever, if you do not rely on the value binding on the `Class.prototype`, that still work.\n\n```javascript\nclass Foo {\n  @accessor({\n    get (value) {\n      return this.baz;\n    },\n    set (value) {\n      this.baz = value;\n    }\n  })\n  bar = 1;\n  baz = 2;\n}\nconst foo1 = new Foo();\nconst foo2 = new Foo();\nfoo2.bar = 3;\nconsole.log(foo1.bar, foo2.bar);\n```\n\nBut it still have a problem. As it was bind on `prototype`, it can't be enumerable.\n\n```javascript\nclass Foo {\n  @accessor({\n    get (value) {\n      return this.baz;\n    },\n    set (value) {\n      this.baz = value;\n    }\n  })\n  bar = 1;\n  baz = 2;\n}\nconst foo = new Foo();\nconsole.log(Object.keys(foo)); // ['baz']\n```\n\nSo, I encourage you to use applyDecorators on InitializeInstanceFields with decorators like [@accessor](#accessor), [@alias](#alias).\n\n## Changelog\n\nPlease read the [realase notes](https://github.com/toxic-johann/toxic-decorators/releases).\n\n## Explanation of Different Build\n\nYou will find four differnet build in the lib.\n\n| Name                        | Kind     | Meaning                                  | Need to define environment |\n| --------------------------- | -------- | ---------------------------------------- | -------------------------- |\n| toxic-decorators.js         | commonjs | Common js, mostly used in Webpack 1.     | Yes                        |\n| toxic-decorators.mjs        | esmodule | in es module, mostly used in webpack 2 and rollup | Yes                        |\n| toxic-decorators.browser.js | umd      | Can be used in browser directly          | No(It's in development)    |\n| toxic-decorators.min.js     | umd      | Can be used in browser directly          | No(It's in production)     |\n\n## Development vs. Production\n\nDevelopment/production modes are hard-coded for the UMD builds: the un-minified files are for development, and the minified files are for production.\n\nCommonJS and ES Module builds are intended for bundlers, therefore we don’t provide minified versions for them. You will be responsible for minifying the final bundle yourself.\n\nCommonJS and ES Module builds also preserve raw checks for `process.env.NODE_ENV` to determine the mode they should run in. You should use appropriate bundler configurations to replace these environment variables in order to control which mode Vue will run in. Replacing `process.env.NODE_ENV` with string literals also allows minifiers like UglifyJS to completely drop the development-only code blocks, reducing final file size.\n\n### Webpack\n\nUse Webpack’s [DefinePlugin](https://webpack.js.org/plugins/define-plugin/):\n\n```\nvar webpack = require('webpack')\n\nmodule.exports = {\n  // ...\n  plugins: [\n    // ...\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: JSON.stringify('production')\n      }\n    })\n  ]\n}\n```\n\n### Rollup\n\nUse [rollup-plugin-replace](https://github.com/rollup/rollup-plugin-replace):\n\n```\nconst replace = require('rollup-plugin-replace')\n\nrollup({\n  // ...\n  plugins: [\n    replace({\n      'process.env.NODE_ENV': JSON.stringify('production')\n    })\n  ]\n}).then(...)\n```\n\n## License\n\n[MIT](https://opensource.org/licenses/MIT)\n\n## Donation\n\nYou can donation to us, that will help us to keep on moving.\n\n![image](https://user-images.githubusercontent.com/2577157/74649176-0a7db980-51ba-11ea-9db4-4dea2e72c6be.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoxic-johann%2Ftoxic-decorators","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoxic-johann%2Ftoxic-decorators","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoxic-johann%2Ftoxic-decorators/lists"}