├── index.js ├── .github └── FUNDING.yml ├── old ├── objectKeys.js ├── selfModification.js ├── defineProperty.js ├── tryEval.js └── comparison.js ├── symbol ├── intro.js ├── globalSymbol.js └── objectKeys.js ├── reflect ├── reflectDeleteProp.js ├── reflectDefineProperty.js └── privateProperty.js ├── proxy ├── logger.js ├── deptDeleteProxy.js ├── employeeGetProxy.js └── userSetProxy.js ├── index.html ├── README.md ├── LICENSE └── .gitignore /index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [atapas] 4 | -------------------------------------------------------------------------------- /old/objectKeys.js: -------------------------------------------------------------------------------- 1 | 2 | const users = { 3 | 'Tom': 32, 4 | 'Bill': 50, 5 | 'Sam': 65 6 | }; 7 | 8 | Object.keys(users).forEach(name => { 9 | const age = users[name]; 10 | console.log(`User ${name} is ${age} years old!`); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /symbol/intro.js: -------------------------------------------------------------------------------- 1 | 2 | // Intro 3 | console.log(Symbol()); 4 | console.log(typeof Symbol() === 'symbol'); 5 | // Uncommenting below line will throw... 6 | // new Symbol(); 7 | 8 | 9 | // debuggability 10 | console.log(Symbol('test')); 11 | console.log(Symbol('test').toString()); 12 | -------------------------------------------------------------------------------- /old/selfModification.js: -------------------------------------------------------------------------------- 1 | 2 | const blog = { 3 | name: 'greenroots' 4 | } 5 | console.log('Before:', blog); 6 | 7 | const key = 'Owner'; 8 | const value = 'Tapas'; 9 | test = () => blog[key] = value; 10 | 11 | // Call the function 12 | test(); 13 | console.log('After:', blog); 14 | 15 | 16 | -------------------------------------------------------------------------------- /reflect/reflectDeleteProp.js: -------------------------------------------------------------------------------- 1 | const obj = { bar: true, baz: false}; 2 | 3 | // delete object[key] 4 | function deleteProperty(object, key) { 5 | delete object[key]; 6 | } 7 | deleteProperty(obj, 'bar'); 8 | 9 | // With Reflect API 10 | Reflect.deleteProperty(obj, 'bar'); 11 | 12 | console.log(obj); 13 | 14 | -------------------------------------------------------------------------------- /old/defineProperty.js: -------------------------------------------------------------------------------- 1 | 2 | const sun = {}; 3 | 4 | Object.defineProperty(sun, 'rises', { 5 | value: true, 6 | configurable: false, 7 | writable: false, 8 | enumerable: false 9 | }); 10 | 11 | console.log('sun rises', sun.rises); 12 | sun.rises = false; 13 | console.log('sun rises', sun.rises); 14 | 15 | -------------------------------------------------------------------------------- /old/tryEval.js: -------------------------------------------------------------------------------- 1 | 2 | const blog = { 3 | name: 'greenroots' 4 | } 5 | console.log('Before eval:', blog); 6 | 7 | const key = 'Owner'; 8 | const value = 'Tapas'; 9 | testEval = () => eval(`blog.${key} = '${value}'`); 10 | 11 | // Call the function 12 | testEval(); 13 | 14 | console.log('After eval magic:', blog); 15 | 16 | -------------------------------------------------------------------------------- /reflect/reflectDefineProperty.js: -------------------------------------------------------------------------------- 1 | const sun = {}; 2 | 3 | const result = Reflect.defineProperty(sun, 'canTalk', { 4 | value: true, 5 | configurable: false, 6 | writable: false, 7 | enumerable: false 8 | }); 9 | 10 | if (result) { 11 | console.log('girl.canTalk', sun.rises); 12 | 13 | sun.rises = false; 14 | 15 | console.log('girl.canTalk', sun.rises); 16 | } 17 | -------------------------------------------------------------------------------- /symbol/globalSymbol.js: -------------------------------------------------------------------------------- 1 | console.log(Symbol('test') === Symbol('test')); 2 | 3 | console.log(Symbol.for('test') === Symbol.for('test')); 4 | 5 | let myObj = {}; 6 | 7 | let firstSymbol = Symbol.for('first'); 8 | let anotherFirstSymbol = Symbol.for('first'); 9 | myObj[firstSymbol] = 'First'; 10 | myObj[anotherFirstSymbol] = 'another first'; 11 | 12 | console.log(myObj[Symbol.for('first')]); 13 | 14 | -------------------------------------------------------------------------------- /old/comparison.js: -------------------------------------------------------------------------------- 1 | // Object.defineProperty() 2 | try { 3 | Object.defineProperty(obj, name, desc); 4 | // property defined successfully 5 | } catch (e) { 6 | // possible failure and need to 7 | // do something about it 8 | } 9 | 10 | // With Reflect API 11 | if (Reflect.defineProperty(obj, name, desc)) { 12 | // success 13 | } else { 14 | // failure (and far better) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /proxy/logger.js: -------------------------------------------------------------------------------- 1 | const employee = { 2 | firstName: 'Tapas', 3 | lastName: 'Adhikary' 4 | }; 5 | 6 | let logHandler = { 7 | get: function(target, fieldName) { 8 | console.log("Log: ", target[fieldName]); 9 | return Reflect.get(target, fieldName); 10 | } 11 | }; 12 | 13 | let func = () => { 14 | let p = new Proxy(employee, logHandler); 15 | p.firstName; 16 | p.lastName; 17 | }; 18 | 19 | func(); 20 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Metaprogramming: Private Variable Test using Proxy and Reflect!

11 | 12 |

Press F12 to open Chrome debugger Console to see output and Debug...

13 | 14 | 17 | 18 | 19 | 20 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-mtprog 2 | Sample code for *Metaprogramming* in JavaScript using ES6 Symbol, Proxy, Reflection etc. Please clone and run this code to learn more. 3 | 4 | # ⭐ Show your support 5 | Show your support with a star(⭐) if you liked the project. You are welcome to follow 🤝 me here to stay connected. 6 | 7 | ## Many Thanks to all the `Stargazers` who has supported this project with stars(⭐) 8 | [![Stargazers repo roster for @atapas/js-mtprog](https://reporoster.com/stars/atapas/js-mtprog)](https://github.com/atapas/js-mtprog/stargazers) 9 | 10 | 11 | Know about [my other projects from here](https://github.com/atapas#-my-show-off-projects). 12 | -------------------------------------------------------------------------------- /proxy/deptDeleteProxy.js: -------------------------------------------------------------------------------- 1 | let department = { 2 | id: 'Dept-001', 3 | name: 'Sales', 4 | manager: 'Tapas Adhikary' 5 | } 6 | 7 | console.group('Department'); 8 | console.log(department.id); 9 | console.log(department.name); 10 | console.log(department.manager); 11 | console.groupEnd(); 12 | 13 | // delete department.id; 14 | // console.log(department); 15 | 16 | const deleteProxy = { 17 | deleteProperty: function(target, fieldName) { 18 | if(fieldName === 'id') return false; 19 | 20 | delete target[fieldName]; 21 | return true; 22 | } 23 | } 24 | 25 | let p = new Proxy(department, deleteProxy); 26 | 27 | delete p.id; 28 | console.log(department); 29 | -------------------------------------------------------------------------------- /symbol/objectKeys.js: -------------------------------------------------------------------------------- 1 | // Use of Symbols as Object Keys 2 | 3 | let myObj = {}; 4 | let firstSymb = Symbol('First'); 5 | let secondSymb = Symbol('Second'); 6 | 7 | myObj[firstSymb] = 'This is First Symbol'; 8 | myObj[secondSymb] = 'This is Second Symbol'; 9 | myObj[Symbol('test')] = 'This is test Symbol'; 10 | myObj['simpleKey'] = 'This is a Simple Key'; 11 | 12 | console.log(myObj); 13 | 14 | // To retrieve: 15 | console.log(myObj[firstSymb]); 16 | console.log(myObj[secondSymb]); 17 | console.log(myObj[Symbol('test')]); // undefined, why? Because, 18 | console.log(Symbol('test') === Symbol('test')); 19 | 20 | // Iterate 21 | console.log(Object.keys(myObj)); 22 | console.log(Object.getOwnPropertyNames(myObj)); 23 | console.log(Object.getOwnPropertySymbols(myObj)); -------------------------------------------------------------------------------- /proxy/employeeGetProxy.js: -------------------------------------------------------------------------------- 1 | const employee = { 2 | firstName: 'Tapas', 3 | lastName: 'Adhikary' 4 | }; 5 | 6 | console.group('employee'); 7 | console.log(employee.firstName); 8 | console.log(employee.lastName); 9 | console.log(employee.org); 10 | console.log(employee.fullName); 11 | console.groupEnd() 12 | 13 | // Now let me use the Proxy concept to get a readble error message and also 14 | // override to get the value for fullName Property. 15 | 16 | let handler = { 17 | get: function(target, fieldName) { 18 | 19 | if(fieldName === 'fullName' ) { 20 | return `${target.firstName} ${target.lastName}`; 21 | } 22 | 23 | return fieldName in target ? 24 | target[fieldName] : 25 | `We do not find the Employee Information for '${fieldName}'!` 26 | 27 | } 28 | }; 29 | 30 | let p = new Proxy(employee, handler); 31 | 32 | console.group('proxy'); 33 | console.log(p.firstName); 34 | console.log(p.lastName); 35 | console.log(p.org); 36 | console.log(p.fullName); 37 | console.groupEnd() 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tapas Adhikary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /proxy/userSetProxy.js: -------------------------------------------------------------------------------- 1 | let user = { 2 | firstName: 'Tapas', 3 | lastName: 'Adhikary' 4 | }; 5 | 6 | user.age = 25; 7 | 8 | console.group('User'); 9 | console.log(user.firstName); 10 | console.log(user.lastName); 11 | console.log(user.age); 12 | console.groupEnd(); 13 | 14 | user.age = 'Something Blunder!' 15 | console.log(user.age); 16 | 17 | user.age = -100 18 | console.log(user.age); 19 | 20 | 21 | // Now let me use the Proxy concept to add Vaiodations 22 | 23 | // This is same Proxy Handler but we named it as 'validator' :-) 24 | const validator = { 25 | set: function(obj, prop, value) { 26 | if (prop === 'age') { 27 | if(!Number.isInteger(value)) { 28 | throw new TypeError('Age is always an Integer, Please Correct it!'); 29 | } 30 | if(value < 0) { 31 | throw new TypeError('This is insane, a negative age?'); 32 | } 33 | } 34 | } 35 | }; 36 | 37 | // Let's create the Proxy 38 | 39 | let p = new Proxy(user, validator); 40 | p.age = 'I am testing the blunder'; 41 | 42 | // test this one by commenting the above line. 43 | p.age = -1; 44 | 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /reflect/privateProperty.js: -------------------------------------------------------------------------------- 1 | function privateProps(obj, filterFunc) { 2 | const handler = { 3 | get (obj, prop) { 4 | if (!filterFunc(prop)) { 5 | let value = Reflect.get(obj, prop); 6 | // auto-bind the methods to the original object, so they will have unrestricted access to it via 'this'. 7 | if (typeof value === 'function') { 8 | value = value.bind(obj); 9 | } 10 | return value; 11 | } 12 | }, 13 | set (obj, prop, value) { 14 | if (filterFunc(prop)) { 15 | throw new TypeError(`Can't set property "${prop}"`); 16 | } 17 | return Reflect.set(obj, prop, value); 18 | }, 19 | has (obj, prop) { 20 | return filterFunc(prop) ? false : Reflect.has(obj, prop); 21 | }, 22 | ownKeys (obj) { 23 | return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop)); 24 | }, 25 | getOwnPropertyDescriptor (obj, prop) { 26 | return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop); 27 | } 28 | }; 29 | return new Proxy(obj, handler); 30 | } 31 | 32 | // trying it out 33 | function propFilter(prop) { 34 | return prop.indexOf('_') === 0; 35 | } 36 | 37 | const myObj = { 38 | _private: 'secret', 39 | public: 'hello', 40 | method: function () { 41 | console.log(this._private); 42 | } 43 | }, 44 | 45 | myProxy = privateProps(myObj, propFilter); 46 | 47 | console.log(myProxy); // chrome somehow logs the private prop, node doesn't 48 | console.log(JSON.stringify(myProxy)); // {"public":"hello"} 49 | console.log(myProxy._private); // undefined - not accessible from outside 50 | myProxy.method(); // secret - accessible from methods 51 | console.log('_private' in myProxy); // false 52 | console.log(Object.keys(myProxy)); // ["public", "method"] 53 | for (let prop in myProxy) { console.log(prop); } // public, method 54 | try { 55 | myProxy._private = 'chicken attack'; // TypeError: Can't set property "_private" 56 | } catch(ex) { 57 | console.error(ex); 58 | } 59 | --------------------------------------------------------------------------------