Access function by dot path without disconnecting `this` (maintain `this` on destructure) - javascript

I have a function that executes functions in a scope. Super simplified case:
function exec(method_name, ...args) {
scope[method_name](...args);
}
Now i have a need to execute a method in an object in the scope. The object is named foo and I need to execute foo.bar().
When I do this:
class FooBar {
constructor() {
this.msg = 'hiii';
this.bar = this.bar.bind(this);
}
bar()
alert(this.msg);
}
}
var foo = new FooBar;
const deepAccessUsingString = (obj, dotpath) => dotpath.split('.').reduce((nested, key) => nested[key], obj);
function exec(mdotpath, ...args) {
deepAccessUsingString(scope, mdotpath)(...args);
}
exec('foo.bar');
I am getting disconnected from this. If I do foo.bar() it works perfectly.
Is there any way other then hard coding in different cases for number of arguments like this below:
let md = mdotpath.split('.');
switch(md.length) {
case 1: return scope[md[0]];
case 2: return scope[md[0]][md[1]]();
case 3: return scope[md[0]][md[1]][md[2]]();
default: throw new Error('not yet supported, you need to hard code in for length of ' + md.length)
}

Related

'hook' types out of anonymous functions

Is there any way to 'get' a class/function/variable that's declared inside a function? example:
() => {
class foo{
}
const bar = () =>
{
console.log("Can print from outside?")
}
}
//Any way to make this possible?:
console.log(new foo())
bar()
Note: Can't change the anonymous function (injection related)
JS is lexical scope, you can't access a local variable outside its function.
you may wanna check Closures
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
This is really hacky!!, but if you don't mind another instance of the code you can just get the current source and wrap inside a function that exposes the foo & bar..
eg..
const anom = () => {
class foo{
}
const bar = () =>
{
console.log("Can print from outside?")
}
}
let code = anom.toString();
code =
code.slice(
code.indexOf('{') + 1,
code.lastIndexOf('}')
) + 'return {foo, bar}';
const f = new Function(code);
const {foo, bar} = f();
bar();
var fooInst = new foo();
console.log(fooInst);

Javascript: Passing a method that is callable with a "this" object

class Foo {
constructor(bar) {
this.bar = bar
}
getBar() {
return this.bar
}
}
function unmethodize(f) {
return function(object, ...args) {
return f.apply(object, args)
}
}
const unmethodizedGetBar = unmethodize(Foo.prototype.getBar)
function test() {
foos = [new Foo(1), new Foo(2), new Foo(3)]
return foos.map(unmethodizedGetBar)
}
I know about foos.map(foo => foo.getBar()) etc.
I simply want a version of getBar that takes the "this" object as its first parameter. Does it already exist somewhere, or must I create it via unmethodize or some such?
Does it already exist somewhere?
No, you'll have to create it yourself. The getBar in your class defines only a method that expects a this argument.
If you don't want to use an arrow function or write your own unmethodize function, you can however achieve this with builtins only:
const unmethodize = Function.bind.bind(Function.call);
foos.map(Function.call.bind(Foo.prototype.getBar))
foos.map(Function.call, Foo.prototype.getBar)
But seriously just use the arrow function :-)

Generically apply context to method aliases

I an encountering a problem which I can't solve. I don't know whether it is a lack knowledge, or the fact that it is not even possible in Javascript, but I hope to get to know it.
I am trying to execute a list of function aliases in an object. When executing these functions, I would like to use them as if they were executed right from the instance itself, So I can use other methods and instance variables within that called method. To make my explaination a bit more clear, here is an example:
class Bar {
constructor() {
this.name = "Bar";
}
someMethod() {
console.log(this.name) // should log Bar
}
}
class Foo {
constructor() {
this.name = "Foo";
}
someOtherMethod() {
console.log(this.name) // should log Foo
}
}
const bar = new Bar();
const foo = new Foo();
const methodList = {
foo: bar.someMethod,
baz: foo.someOtherMethod,
}
for(let prop in methodList) {
methodList[prop](); // logs 2x undefined
}
for(let prop in methodList) {
methodList[prop].apply(foo); //logs 2x Foo
}
As can be seen in the example above, this.name is a variable in the instance of the class. When executing the second loop, a context is applied and logs correctly, as expected. I would like to see that context being applied automatically, since the function alias object is executed in a different file, not knowing about foo or bar and just receiving the list.
Is there any way to achieve this?
You could wrap your foo and bar methods in a function of their own. Within these methods you could then call the object's method someMethod()/someOtherMethod() on the object like so:
const methodList = {
foo: (...args) => bar.someMethod(...args),
baz: (...args) => foo.someOtherMethod(...args),
}
At the moment your first loop doesn't work because your this doesn't refer to the actual context of the object as that isn't what is used to invoke the method. It instead refers to your methodList
See example bellow:
class Bar {
constructor() {
this.name = "Bar";
}
someMethod() {
console.log(this.name) // should log Bar
}
}
class Foo {
constructor() {
this.name = "Foo";
}
someOtherMethod() {
console.log(this.name) // should log Foo
}
}
const bar = new Bar();
const foo = new Foo();
const methodList = {
foo: (...args) => bar.someMethod(...args),
baz: (...args) => foo.someOtherMethod(...args),
}
for(let prop in methodList) {
methodList[prop](); // correct logs
}
This happens because the contextual this when invoking methodList[prop]
is methodList, hence this in someMethod and someOtherMethod is actually:
{
foo: bar.someMethod,
baz: foo.someOtherMethod,
}
To solve the issue, you can wrap the methods in an anonymous function returning the invoked method, as shown below:
class Bar {
constructor() {
this.name = "Bar";
}
someMethod(a,b,c) {
console.log(a,b,c,this.name) // should log Bar
}
}
class Foo {
constructor() {
this.name = "Foo";
}
someOtherMethod(a,b,c) {
console.log(a,b,c,this.name) // should log Foo
}
}
const bar = new Bar();
const foo = new Foo();
const methodList = {
foo: (...args) => bar.someMethod(...args), // <-- anonymous function that, once invoked, returns `bar.someMethod()`, hence the contextual `this` of someMethod will be `bar`.
baz: function() { // <-- same as above, just written without the lambda notation.
return foo.someOtherMethod(...arguments);
}//^
}// | <-- that evaluation is actually calling the above code block.
// |-------------------------------------|
for(let prop in methodList) {// |
methodList[prop](1,4,'hello'); // <------|
}

What different between assigning property on Object and Object.prototype?

What different between assigning property on Object and Object.prototype?
for example
Object.test =function(){};
and
Object.prototype.test =function(){}
The first gives Object a static method that can be invoked directly from the class, without an instance. For example:
Object.test =function(){
console.log('Object test running');
};
Object.test();
Assigning a function to the prototype, on the other hand, allows for instances to run the method:
Object.prototype.test = function() {
console.log('test running on object ', this);
};
// don't use the object constructor, this is just an example:
const obj = new Object();
obj.test();
It might make a bit more sense if you didn't use the built-in Object, which everything inherits from:
function Foo() {}
Foo.checkIfFoo = function(arg) {
return arg instanceof Foo;
};
const f = new Foo();
console.log(Foo.checkIfFoo(f));
Here, foo.checkIfFoo is a helper function on Foo that checks if a passed object is an instance of Foo or not - no instance is required to run checkIfFoo. Functions on the prototype, on the other hand, require an instance to run:
function Foo() {
this.info = 'A Foo instance';
}
Foo.prototype.checkInfo = function() {
console.log(this.info);
};
const f = new Foo();
f.checkInfo();
Note that in ES6+, you can put a function directly on the class with the static keyword:
// roughly equivalent to the snippet with checkIfFoo above
class Foo {
static checkIfFoo(arg) {
return arg instanceof Foo;
}
}
const f = new Foo();
console.log(Foo.checkIfFoo(f));
Whereas a standard method lacks the static keyword:
// roughly equivalent to the snippet with checkInfo above
class Foo {
constructor() {
this.info = 'A Foo instance';
}
checkInfo() {
console.log(this.info);
}
}
const f = new Foo();
f.checkInfo();

How do I make a constructor have accessible members when its called as an object

I can do this in JavaScript:
var output = String(result);
And I can do this with the same object that is referenced using String:
var character = String.fromCharCode(10);
String can be used as a function to construct an object and members can be called on it without using it as a constructor. How do I make an object usable in both these ways? What is this called?
You are talking about class methods.
function Foo() {
this.bar = 3
}
Foo.baz = function() {
console.log('hi');
}
or in ES 2015
class Foo {
static baz () {
console.log('hi');
}
}
Response to the comments
You can define a static method in the constructor function because the constructor function is necessarily in scope:
function Foo() {
Foo.method = function () {
// do stuff
}
}
There are a couple of problems with this approach though:
Foo.method('stringy string'); // TypeError: cannot read property 'method' of undefined
Because the static method is defined in the constructor, it won't be there until the constructor function runs at least once:
const foo = new Foo();
Foo.method('stringy string'); // now we're good
which leads to another problem, now we're wastefully reassigning that method every time the constructor runs. You can avoid that with a conditional check:
function Foo() {
if (!Foo.method) Foo.method = function....
}
But that's a lot of weird stuff just to avoid defining the class method after the constructor, and it still doesn't solve the first problem.
You can make a class with static methods:
class Foo {
constructor(bar) {
this.bar = bar;
}
length() {
return this.bar.length;
}
static fromThing(thing) {
return new Foo(thing.bar);
}
}
Foo.fromThing() is analogous to String.fromCharCode()
Simply
function MyClass () {
this.val = 1;
}
MyClass.staticMethod = function () {/* code here */};

Categories

Resources