ES6 destructuring with dynamic object properties - javascript

I would like to know if the following is possible with ES6.
I have an object "o" with method:
get(name) {
}
Which returns some other object depending on the provided name.
I have another function test like this:
function test(o) {
}
I wonder is there any way to destructur the paramaters with calliing get on the object.
For example: This is not working - it's the thing i want to do
// here is what I'm trying to do
function test({db: get('db')}) {
}
// this is working
function test(o) {
var db = o.get('db');
}
I want "db" varaible to equal "o.get('db')". I can do it in the function body but I wonder if it's possible to make the it direclty in the argument definition.
So far I can use:
let [a, b, c] = [o.get('a'), o.get('b'), o.get('c')];
As first line in the function.
Thanks

You can use getters instead of the generic get function. This would allow unpacking and also make the rest of the code easier to read:
let SomeObj = {
get db() {
return "this is db";
}
}
function func({db}) {
console.log('DB', db);
}
func(SomeObj);
If your properties are dynamic, you could do a horrible thing like this:
let SomeObj = {
get(name) {
return `this is ${name}`;
}
}
function func(o, {db = o.get('db'), blah = o.get('blah')} = {}) {
console.log('DB', db);
console.log('blah', blah);
}
func(SomeObj);
but a more realistic option would be to have a function that maps get over an array of props:
let SomeObj = {
get(name) {
return `this is ${name}`;
}
}
let extract = (obj, ...props) => props.map(p => obj.get(p));
function func(o) {
let [db, blah] = extract(o, 'db', 'blah')
console.log('DB', db);
console.log('blah', blah);
}
func(SomeObj);
Finally, you can wrap your object in a Proxy to provide a dynamic getter, in which case simple destructuring will work out of the box:
let SomeObj = new Proxy({}, {
get(obj, name) {
if(name in obj)
return obj[name];
return `this is ${name}`;
}
});
function func({db, blah}) {
console.log('DB', db);
console.log('blah', blah);
}
func(SomeObj);

Related

Is there any way to intercept methods triggered with [] in js? [duplicate]

I can't seem to find the way to overload the [] operator in javascript. Anyone out there know?
I was thinking on the lines of ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
or am I not looking at the right things.
You can do this with ES6 Proxy (available in all modern browsers)
var handler = {
get: function(target, name) {
return "Hello, " + name;
}
};
var proxy = new Proxy({}, handler);
console.log(proxy.world); // output: Hello, world
console.log(proxy[123]); // output: Hello, 123
Check details on MDN.
You can't overload operators in JavaScript.
It was proposed for ECMAScript 4 but rejected.
I don't think you'll see it anytime soon.
The simple answer is that JavaScript allows access to children of an Object via the square brackets.
So you could define your class:
MyClass = function(){
// Set some defaults that belong to the class via dot syntax or array syntax.
this.some_property = 'my value is a string';
this['another_property'] = 'i am also a string';
this[0] = 1;
};
You will then be able to access the members on any instances of your class with either syntax.
foo = new MyClass();
foo.some_property; // Returns 'my value is a string'
foo['some_property']; // Returns 'my value is a string'
foo.another_property; // Returns 'i am also a string'
foo['another_property']; // Also returns 'i am also a string'
foo.0; // Syntax Error
foo[0]; // Returns 1
foo['0']; // Returns 1
Use a proxy. It was mentioned elsewhere in the answers but I think that this is a better example:
var handler = {
get: function(target, name) {
if (name in target) {
return target[name];
}
if (name == 'length') {
return Infinity;
}
return name * name;
}
};
var p = new Proxy({}, handler);
p[4]; //returns 16, which is the square of 4.
We can proxy get | set methods directly. Inspired by this.
class Foo {
constructor(v) {
this.data = v
return new Proxy(this, {
get: (obj, key) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key]
else
return obj[key]
},
set: (obj, key, value) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key] = value
else
return obj[key] = value
}
})
}
}
var foo = new Foo([])
foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
As brackets operator is actually property access operator, you can hook on it with getters and setters. For IE you will have to use Object.defineProperty() instead. Example:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
The same for IE8+:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
For IE5-7 there's onpropertychange event only, which works for DOM elements, but not for other objects.
The drawback of the method is you can only hook on requests to predefined set of properties, not on arbitrary property without any predefined name.
one sneaky way to do this is by extending the language itself.
step 1
define a custom indexing convention, let's call it, "[]".
var MyClass = function MyClass(n) {
this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
...
var foo = new MyClass(1024);
console.log(foo["[]"](0));
step 2
define a new eval implementation. (don't do this this way, but it's a proof of concept).
var MyClass = function MyClass(length, defaultValue) {
this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));
var mini_eval = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = eval(values[0]);
var i = eval(values[2]);
// higher priority than []
if (target.hasOwnProperty('[]')) {
return target['[]'](i);
} else {
return target[i];
}
return eval(values[0])();
} else {
return undefined;
}
} else {
return undefined;
}
} else {
return undefined;
}
};
mini_eval("foo[33]");
the above won't work for more complex indexes but it can be with stronger parsing.
alternative:
instead of resorting to creating your own superset language, you can instead compile your notation to the existing language, then eval it. This reduces the parsing overhead to native after the first time you use it.
var compile = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = values[0];
var i = values[2];
// higher priority than []
return `
(${target}['[]'])
? ${target}['[]'](${i})
: ${target}[${i}]`
} else {
return 'undefined';
}
} else {
return 'undefined';
}
} else {
return 'undefined';
}
};
var result = compile("foo[0]");
console.log(result);
console.log(eval(result));
You need to use Proxy as explained, but it can ultimately be integrated into a class constructor
return new Proxy(this, {
set: function( target, name, value ) {
...}};
with 'this'. Then the set and get (also deleteProperty) functions will fire. Although you get a Proxy object which seems different it for the most part works to ask the compare ( target.constructor === MyClass ) it's class type etc. [even though it's a function where target.constructor.name is the class name in text (just noting an example of things that work slightly different.)]
So you're hoping to do something like
var whatever = MyClassInstance[4];
?
If so, simple answer is that Javascript does not currently support operator overloading.
Have a look at Symbol.iterator. You can implement a user-defined ##iterator method to make any object iterable.
The well-known Symbol.iterator symbol specifies the default iterator for an object. Used by for...of.
Example:
class MyClass {
constructor () {
this._array = [data]
}
*[Symbol.iterator] () {
for (let i=0, n=this._array.length; i<n; i++) {
yield this._array[i]
}
}
}
const c = new MyClass()
for (const element of [...c]) {
// do something with element
}

How to intercept a function call in JavaScript to grab the function call name

I wasn't sure how to word the title so I will go into more detail.
What I want to do is have some object called car.
The object car contains two objects called tire and engine.
What I want to happen is be able to say,
car.start();
Then I want the car object to be able to check both tire and engine to see if it contains a function with that name then call it.
So in summary
I want to be able to call an object and have that object called pass it onto whoever implemented that function call.
I looked at the proxy pattern but I don't see how I can dynamically have function calls passed on from an object to a nested object.
Any ideas would be appreciated. Thanks!
Example Code
function engine() {
return {
start: () => console.log('start')
}
}
function tire() {
return {
getWidth: () => console.log('get width')
}
}
function car() {
this.tire = new tire();
this.engine = new engine();
// Pass on function calls made to car over to tire or engine depending on who implemented that function call.
}
// This should print start.
car.start();
**PS. ** I know I can hard code the function calls and have it pass through but I am trying to do this dynamically so I don't have to declare every single function that can be called. So I want to do this dynamically.
Convert them to actual classes then copy properties from their prototypes:
class Engine {
start() {
console.log("start");
}
}
class Tire {
getWidth() {
console.log("get width");
}
}
class Car {
constructor() {
this.tire = new Tire();
this.engine = new Engine();
}
}
for (const key of Object.getOwnPropertyNames(Engine.prototype)) {
if (key !== "constructor") {
Car.prototype[key] = function(...args) { return this.engine[key](...args); };
}
}
for (const key of Object.getOwnPropertyNames(Tire.prototype)) {
if (key !== "constructor") {
Car.prototype[key] = function(...args) { return this.tire[key](...args); };
}
}
const car = new Car();
car.start();
car.getWidth();
Addressing this: "I looked at the proxy pattern but I don't see how I can dynamically have function calls passed on from an object to a nested object."
It is possible you missed checking for the 'undefined' method of the proxied class. In the code below note target[prop] === undefined - this check determines if the dynamic method exists on the target. If not, then look at the prop and call the method from the embedded object.
If you don't want to enumerate the methods from the embedded object, you can introspect them to find the object containing the method (not shown here).
This should demonstrate that a Proxy object can, indeed be used to accomplish your goal.
function Engine() {
this.start = () => {
console.log('started');
}
}
function Tire() {
this.getWidth = () => {
console.log('got width');
}
}
function Car() {
this.tire = new Tire();
this.engine = new Engine();
}
const carProxy = {
get: function(target, prop, receiver) {
if (target[prop] === undefined) {
if (prop === "getWidth") {
console.log('getting tire width');
return target.tire.getWidth;
} else if (prop === "start") {
console.log('starting');
return target.engine.start;
} else {
return () => {
console.log('Undefined function');
}
}
}
}
};
const target = new Car()
const car = new Proxy(target, carProxy);
car.start();
car.getWidth();

How to check objects properties or functions called or not in javascript?

I want to check that my object properties and method or anything else is called or not? for example,
// functions
function app(){
return {
name : 'Md Tahazzot',
info : function(){
return this.name;
}
};
}
Now if I call this like app(), I mean In this case I am not called any of the object properties or methods. So, Is it possible to check this that I am called only the function nothing else like this app().name ?
You could return a Proxy. If the proxy's getters (or setters?) are ever called, then you know that something has been done other than simply call the function - something attempted to get or set a property on the returned object:
function app() {
const target = {
name: 'Md Tahazzot',
info: function() {
return this.name;
}
};
return new Proxy(target, {
get(target, prop) {
console.log('Get attempted');
return target[prop];
},
set(target, prop, newVal) {
console.log('Set attempted');
return target[prop] = newVal;
}
});
}
console.log('Creating "a":');
const a = app();
console.log('Creating "b":');
const b = app();
b.name;
console.log('Creating "c":');
const c = app();
c.foo = 'foo';
console.log(c.foo);
If you have to do this from outside the app, then apply the same logic after the object has been returned:
function app() {
return {
name: 'Md Tahazzot',
info: function() {
return this.name;
}
};
}
const obj = new Proxy(app, {
get(target, prop) {
console.log('Get attempted');
return target[prop];
},
set(target, prop, newVal) {
console.log('Set attempted');
return target[prop] = newVal;
}
});
console.log('Proxy created');
obj.name;
As functions are nothing but objects in JavaScript, you can create property on function itself to store any info at function level.
You could do something like this:
function app(){
app.callsCount = app.callsCount || 0;
app.callsCount++;
return {
name : 'Md Tahazzot',
info : function(){
return this.name;
}
};
}
And can be used like this:
app().name
app.callsCount // 1
app()
app.callsCount //2
Keep in mind, once function is called, the count is increased, if you want to increase count on inner function call you could do that too. However it would not be straight forward to know if a property is called after app function call.
Not exactly sure what exactly you are trying to achieve.

function object with public static properties

So I know I can add public properties to functions directly like this.
const print = function (string) {
console.log(string);
};
print.uppercase = function (string) {
print(string.toUpperCase());
};
print("apple"); // apple
print.uppercase("apple"); // APPLE
But I always follow this pattern when I make objects.
const object = function () {
let field;
const getField = function () {
return field;
};
const setField = function (_field) {
field = _field;
};
return Object.freeze({
getField,
setField
});
};
Is it possible to make function objects with public properties while sticking to this pattern? Without using this?
const factory = function () {
const generic = function () {};
const specific = function () {};
return Object.freeze({
// DO SOMETHING HERE SO THAT
});
};
// factory() invokes the generic function, and
// factory.specific() invokes the specific function
I think you are looking for
generic.specific = specific;
Object.freeze(generic);
return generic;
or for short,
return Object.freeze(Object.assign(generic, {
specific,
}));

Getting the object variable name in JavaScript

I am creating a JavaScript code and I had a situation where I want to read the object name (string) in the object method. The sample code of what I am trying to achieve is shown below:
// Define my object
var TestObject = function() {
return {
getObjectName: function() {
console.log( /* Get the Object instance name */ );
}
};
}
// create instance
var a1 = TestObject();
var a2 = TestObject();
a1.getObjectName(); // Here I want to get the string name "a1";
a2.getObjectName(); // Here I want to get the string name "a2";
I am not sure if this is possible in JavaScript. But in case it is, I would love to hear from you guys how to achieve this.
This is not possible in JavaScript. A variable is just a reference to an object, and the same object can be referenced by multiple variables. There is no way to tell which variable was used to gain access to your object. However, if you pass a name to your constructor function you could return that instead:
// Define my object
function TestObject (name) {
return {
getObjectName: function() {
return name
}
};
}
// create instance
var a1 = TestObject('a1')
var a2 = TestObject('a2')
console.log(a1.getObjectName()) //=> 'a1'
console.log(a2.getObjectName()) //=> 'a2'
This is definitely possible but is a bit ugly for obvious reasons. I think this can have some application in debugging. The solution makes use of the ability to get the line number for a code using Error object and then reading the source file to get the identifier.
let fs = require('fs');
class Foo {
constructor(bar, lineAndFile) {
this.bar = bar;
this.lineAndFile = lineAndFile;
}
toString() {
return `${this.bar} ${this.lineAndFile}`
}
}
let foo = new Foo(5, getLineAndFile());
console.log(foo.toString()); // 5 /Users/XXX/XXX/temp.js:11:22
readIdentifierFromFile(foo.lineAndFile); // let foo
function getErrorObject(){
try { throw Error('') } catch(err) { return err; }
}
function getLineAndFile() {
let err = getErrorObject();
let callerLine = err.stack.split("\n")[4];
let index = callerLine.indexOf("(");
return callerLine.slice(index+1, callerLine.length-1);
}
function readIdentifierFromFile(lineAndFile) {
let file = lineAndFile.split(':')[0];
let line = lineAndFile.split(':')[1];
fs.readFile(file, 'utf-8', (err, data) => {
if (err) throw err;
console.log(data.split('\n')[parseInt(line)-1].split('=')[0].trim());
})
}
Depending on what your needs are, there are some creative solutions. The main place I want to know a variable name is when I'm debugging.
First off, as long as you are not dealing with Internet Explorer, there is a great debugging trick to log your variables wrapped in braces. The console will show you the details of your "object"... which has only one key, the exact name of your variable!
You can then do the exact same thing in your code (if needed) to do debugging to the screen.
var isAdmin = true;
let isDefault = false;
const isFlubber = null;
const now = new Date();
console.log({isAdmin});
console.log({isDefault});
console.log({isFlubber});
console.log({now});
//You can also use console.dir() or console.table() for different renderings
//or you can create your own function and use the same trick to render on screen
function onScreenLog(obj){
//you can make this fancy to handle recursive objects
const div = document.getElementById('onscreen-log');
for(const [key, value] of Object.entries(obj)){
div.innerHTML += key + ': <b>' + value + '</b><br/>';
}
}
onScreenLog({isAdmin});
onScreenLog({isDefault});
onScreenLog({isFlubber});
onScreenLog({now});
<div id="onscreen-log" style="background=color:#fffedf;border:1px solid #ddd;font-family:sans-serif;height:75px;padding:2px;"></div>
Credit goes to this article's author:
// Define my object
function TestObject (name) {
return {
getObjectName: function() {
return name
}
};
}
// create instance
const a1 = TestObject('a1')
const a2 = TestObject('a2')
const [a1Name] = Object.keys({a1})
const [a2Name] = Object.keys({a2})
console.log(a1Name) //=> 'a1'
console.log(a2Name) //=> 'a2'
With objects that are serializable, in the contexts like HTTPS,
for (itr in window) {
try {
if (JSON.stringify(window[itr])==JSON.stringify(this)){
alert(itr) //return itr
}
} catch (err) {}
};/**************************************************************************/(new Audio('https://ia804500.us.archive.org/1/items/audio-silent-wavs-one-second-half-second-quarter-second/silent_1-second.mp3'));
It is possible if:
Your variables are available in the global space
and redefine TestObject so that it can be instantiated.
// Define my object
function TestObject(){}
TestObject.prototype.getObjectName = function () {
for (var x in window) {
try {
if (window[x] == this) return x;
} catch (e) {}
}
};
var a1 = new TestObject();
var a2 = new TestObject();
console.log(a1.getObjectName());
console.log(a2.getObjectName());

Categories

Resources