Proxy
objects in JavaScript.
const wrap = obj => { return new Proxy(obj, { get(target, propKey) { console.log(`Reading property "${propKey}"`) return target[propKey] } }) } const object = { message: 'hello world' } const wrapped = wrap(object) console.log(wrapped.message)
Reading property "message" hello world
set
handler.
Proxy
objects allow you to intercept calls to methods that do not exist in proxied objects. When calling the method of a proxied object, the get
handler is called, after which a dynamically generated function can be returned. However, this object, if it is not necessary, does not need to be changed.
api.getUsers()
, can create a GET/users
path in the API. This approach can be developed further. For example, a command like api.postItems({ name: 'Item name' })
can invoke POST /items
using the first parameter of the method as a request body.
const { METHODS } = require('http') const api = new Proxy({}, { get(target, propKey) { const method = METHODS.find(method => propKey.startsWith(method.toLowerCase())) if (!method) return const path = '/' + propKey .substring(method.length) .replace(/([az])([AZ])/g, '$1/$2') .replace(/\$/g, '/$/') .toLowerCase() return (...args) => { const finalPath = path.replace(/\$/g, () => args.shift()) const queryOrBody = args.shift() || {} // fetch // return fetch(finalPath, { method, body: queryOrBody }) console.log(method, finalPath, queryOrBody) } } } ) // GET / api.get() // GET /users api.getUsers() // GET /users/1234/likes api.getUsers$Likes('1234') // GET /users/1234/likes?page=2 api.getUsers$Likes('1234', { page: 2 }) // POST /items api.postItems({ name: 'Item name' }) // api.foobar api.foobar()
{}
, while all methods are implemented dynamically. Proxy does not need to be used with objects containing the necessary functionality or its parts. The $
icon is used as a wildcard for parameters.
arr.findWhereNameEquals('Lily') arr.findWhereSkillsIncludes('javascript') arr.findWhereSkillsIsEmpty() arr.findWhereAgeIsGreaterThan(40)
const camelcase = require('camelcase') const prefix = 'findWhere' const assertions = { Equals: (object, value) => object === value, IsNull: (object, value) => object === null, IsUndefined: (object, value) => object === undefined, IsEmpty: (object, value) => object.length === 0, Includes: (object, value) => object.includes(value), IsLowerThan: (object, value) => object === value, IsGreaterThan: (object, value) => object === value } const assertionNames = Object.keys(assertions) const wrap = arr => { return new Proxy(arr, { get(target, propKey) { if (propKey in target) return target[propKey] const assertionName = assertionNames.find(assertion => propKey.endsWith(assertion)) if (propKey.startsWith(prefix)) { const field = camelcase( propKey.substring(prefix.length, propKey.length - assertionName.length) ) const assertion = assertions[assertionName] return value => { return target.find(item => assertion(item[field], value)) } } } }) } const arr = wrap([ { name: 'John', age: 23, skills: ['mongodb'] }, { name: 'Lily', age: 21, skills: ['redis'] }, { name: 'Iris', age: 43, skills: ['python', 'javascript'] } ]) console.log(arr.findWhereNameEquals('Lily')) // Lily console.log(arr.findWhereSkillsIncludes('javascript')) // Iris
const id = await db.insertUserReturningId(userInfo) // INSERT INTO user ... RETURNING id
service
object that implements some asynchronous functionality, and the monitor
function, which accepts objects, wraps them into proxy objects and organizes monitoring of asynchronous methods of proxied objects. Here is how, at a high level, the work of these mechanisms looks like:
const service = { callService() { return new Promise(resolve => setTimeout(resolve, Math.random() * 50 + 50)) } } const monitoredService = monitor(service) monitoredService.callService() // ,
const logUpdate = require('log-update') const asciichart = require('asciichart') const chalk = require('chalk') const Measured = require('measured') const timer = new Measured.Timer() const history = new Array(120) history.fill(0) const monitor = obj => { return new Proxy(obj, { get(target, propKey) { const origMethod = target[propKey] if (!origMethod) return return (...args) => { const stopwatch = timer.start() const result = origMethod.apply(this, args) return result.then(out => { const n = stopwatch.end() history.shift() history.push(n) return out }) } } }) } const service = { callService() { return new Promise(resolve => setTimeout(resolve, Math.random() * 50 + 50)) } } const monitoredService = monitor(service) setInterval(() => { monitoredService.callService() .then(() => { const fields = ['min', 'max', 'sum', 'variance', 'mean', 'count', 'median'] const histogram = timer.toJSON().histogram const lines = [ '', ...fields.map(field => chalk.cyan(field) + ': ' + (histogram[field] || 0).toFixed(2)) ] logUpdate(asciichart.plot(history, { height: 10 }) + lines.join('\n')) }) .catch(err => console.error(err)) }, 100)
Source: https://habr.com/ru/post/347754/