I have a class Notify, for example:
class Notify {
success(text) {
// TODO
}
error(text) {
// Todo
}
}
export default new Notify();
When I'm using I call directly to methods in this class like Notify.success(), So, now I want to try a new way to call it like Notify('my title', 'success'). In PHP I know it's __invoke method, but in JS I don't know how to use it likes that. Can I do that in class? Or I have to use 'normally' functions.
Please help me. Thank you.
There is no analogue of __invoke in JavaScript. You can make a function and then attach properties to is so that it can be used as an object too.
function notifyConstructor() {
// Make the function
const notify = (text, type) => {
switch (type) {
case 'success': return notify.success(text);
case 'error': return notify.error(text);
default: throw TypeError(`Unknown type "${type}"`);
}
};
// Attach public properties and methods to the function
notify.success = text => {
// TODO
};
notify.error = text => {
// Todo
};
return notify;
}
const notify = notifyConstructor(); // Make a Notify instance
notify('my title', 'success'); // Call the instance
notify.success('my title'); // Call an instance method
You can get the functions using the context this as follows.
Be careful if you call the function invoke by itself (Stackoverflow error).
class Notify {
constructor() {
}
invoke(msg, fn) {
if (this[fn]) {
this[fn](msg);
} else throw new Error("Illegal argument Error.");
}
success(text) {
console.log('Success:', text);
}
error(text) {
console.log('Error:', text);
}
}
let notify = new Notify();
notify.invoke('my title', 'success');
notify.invoke('my title', 'error');
notify.invoke('my title', 'ele');
Or, directly over the instantiated object:
class Notify {
constructor() {
}
success(text) {
console.log('Success:', text);
}
error(text) {
console.log('Error:', text);
}
}
let notify = new Notify();
notify['success']('my title');
notify['error']('my title');
In javascript object property access using a dot (.) is short hand notation for accessing the property using array notation ([]). Short hand notation does, however, require the property name to conform to the syntax used for identifiers.
Assuming Notify is the name of the imported instance,
Notify["success"]("my title"]
is equivalent to
Notify.success( "my title")
Related
I have a class that manages a WebSocket connection and provides the methods to subscribe to this WebSocket. An instance of this class may be passed to many child objects that subscribe to this WebSocket via these methods:
const client = new WebSocketManager();
await client.connect();
const objA = new ClassA(client);
const objB = new ClassB(client);
client.close(); // at a future point in time
The problem is that client exposes critical methods like connect and close. If objA calls close, objB would fail for no apparent reason. Those two methods should be private to the children, but public to the initiator of the parent object. Is there a pattern to solve this problem? Two methods are presented below. Are any of those two patterns acceptable or are there better solutions? I would like to hear your opinion.
Method 1: Define an owner of the parent object. Restricted methods require a reference to the owner.
class Parent {
#owner;
constructor(owner) {
this.#owner = owner;
}
restricedMethod(owner) {
if(owner !== this.#owner)
throw Error('not authorized');
console.log('restricedMethod called');
}
accessibleMethod() {
console.log('accessibleMethod called');
}
}
class Child {
constructor(dependency) {
this.dependency = dependency;
}
callAccessibleMethod() {
this.dependency.accessibleMethod();
}
callRestricedMethod() {
this.dependency.restricedMethod();
}
}
const parent = new Parent(this);
// 'this' is the owner of 'parent'
const child = new Child(parent);
child.callAccessibleMethod();
// accessibleMethod called
try { child.callRestricedMethod(); } catch(e) { console.log(e.message); }
// Error: not autherized
parent.restricedMethod(this); // only the owner can call this method
// restricedMethod called
Method 2: Create a new parent object on the fly that only contains the methods that the child may access.
class Parent {
restricedMethod(){
console.log('restricedMethod called');
}
accessibleMethod(){
console.log('accessibleMethod called');
}
}
class Child {
constructor(dependency) {
this.dependency = dependency;
}
callAccessibleMethod() {
this.dependency.accessibleMethod();
}
callRestricedMethod() {
this.dependency.restricedMethod();
}
}
class MethodSelection {
constructor(object, methods) {
for (const method of methods)
this[method] = object[method];
}
}
const parent = new Parent();
// select methods from parent object and create new object
const restricted = new MethodSelection(parent, ['accessibleMethod']);
console.log(restricted.accessibleMethod === parent.accessibleMethod);
// true
const child = new Child(restricted);
child.callAccessibleMethod();
// accessibleMethod called
try { child.callRestricedMethod(); } catch(e) { console.log(e.message); }
// TypeError: this.dependency.restricedMethod is not a function
parent.restricedMethod();
// restricedMethod called
I have two classes, A and B. What I am trying to do is to pass data from A to B after receiving a message from sockets.
This is simplified look of how classes are defined:
class A:
export default class A {
client;
callbacks;
constructor() {
this.callbacks = {
open: () => this.client.logger.debug('open'),
close: () => this.client.logger.debug('closed'),
message: (data) => {this.client.logger.log(data)}, //I want to pass this data object to class B
};
this.client = new Spot(constants.apiKey, constants.apiSecret, {
baseURL: constants.baseURL,
wsURL: constants.wsURL,
});
this.client.userData(listenKey, this.callbacks);
}
}
I already have a property of A in class definition of B:
export default class B {
account;
constructor() {
this.account = new A();
}
}
What would be a correct/standard way to connect these two so I get a 'data' object from class A every time the socket message callback from class A is triggered?
I am a bit new with JS, but on iOS we would use a delegation pattern, with a protocol, that says:
class A will have a delegate property.
A delegate (class B) must implement a protocol (in this case it would be a requirement to implement method called didReceiveMessage(data).
After that, when a message is received in class A, we would just do(in socket message callback shown above) something like this.delegate.didReceiveMessage(data).
Protocol usage here is not important generally, but it is a plus, cause from A class, we can only access didReceiveData(data) method trough a delegate property, and nothing else (other properties / methods of class B are not visible). At least that is how it works in Swift/Obj-C. I just mentioned it, cause I am curious is this how it is done in JS too.
I guess there is some similar mechanism in Javascript, or some more standard/better way to achieve this kind of data sending between objects?
on iOS we would use a delegation pattern, with a protocol
You can do it exactly as you described:
export default class A {
client;
delegate;
constructor(delegate) {
this.delegate = delegate;
this.client = new Spot(constants.apiKey, constants.apiSecret, {
baseURL: constants.baseURL,
wsURL: constants.wsURL,
});
const callbacks = {
open: () => this.client.logger.debug('open'),
close: () => this.client.logger.debug('closed'),
message: (data) => this.delegate.didReceiveMessage(data),
};
this.client.userData(listenKey, callbacks);
}
}
export default class B {
account;
constructor() {
this.account = new A(this);
}
didReceiveMessage(data) {
console.log(data); // or whatever
}
}
There is no interface (protocol) declaration that would tell A which properties and methods it may access on the passed delegate, but the contract exists of course. You should document it in prose. (Or use TypeScript).
Notice also how your class A interacts with the Spot client, it uses very much the same pattern of passing an object with event handler methods.
A simpler pattern in JavaScript, if you just need a single method in your protocol, is to pass a callable function only:
export default class A {
client;
constructor(onMessage) {
this.client = new Spot(constants.apiKey, constants.apiSecret, {
baseURL: constants.baseURL,
wsURL: constants.wsURL,
});
this.client.userData(listenKey, {
open: () => this.client.logger.debug('open'),
close: () => this.client.logger.debug('closed'),
message: onMessage,
});
}
}
export default class B {
account;
constructor() {
this.account = new A(this.didReceiveMessage.bind(this));
// or inline:
this.account = new A(data => {
console.log(data); // or whatever
});
}
didReceiveMessage(data) {
console.log(data); // or whatever
}
}
I am not an expert on NodeJs, but you can use something like an emitter plugin.
In javascript, it would look like this:
function A() {
Emitter(this);
this.action = function() {
console.log("something happened");
this.emit("action", { prop: "value" });
};
}
function B(a_instance) {
// subscribe to "action" event
a.on("action", function(data) {
console.log(data.prop); // "value"
});
};
var myA = new A();
var myB = new B(myA);
myA.action();
I saw this code today, which does something I've never seen before. It has an object which itself has an unlabeled property that is a function.
emails = {
type: EmailType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
}
I'd like to know more about this, but I have no idea what the proper keyterm for this is, and searching "function as property js" only yields really obvious stuff (ie {someProp: () => 42}).
I'm certain that both:
A. If I knew the right key term, it would be really easy to learn more and
B. The only way to make this keyterm easier to find is to have something someone would actually search lead to it. To that end, I'll include some extra SEO:
object has function but not at prop
function inlined in object
function in object
object has a function but it's not a prop
no propname for function
Anyways:
What is this called, and where can I find more information on it?
EDIT: Got links to docs. One thing to denote is the differences between
// these are the same, I think
const eg1 = { someFn() {} }
const eg2 = { someFn: function() {} }
// this is different in scope... I think
const someFn = () => {};
const eg3 = { someFn };
It is a Shorthand method name.
{ method() { /*...*/ } }
is equal to:
{ method: function() { /*...*/ } }
I have these two methods which are almost similar:
private firstFunction () {
this.serviceOne.methodOne().subscribe(
res => {
return resultOne = res;
},
err => {}
);
}
private secondFunction () {
this.serviceTwo.methodTwo().subscribe(
res => {
return resultTwo = res;
},
err => {}
);
}
I want to write a generic function, like this:
genericFunction (service ,method , result ) {
service.method().subscribe(
res => {
return result = res;
},
err => {}
);
}
And consequently I want to get something like this working:
genericFunction (serviceOne , methodOne , resultOne );
genericFunction (serviceTwo , methodTwo , resultTwo );
Actually, I cannot find how to pass methodOne and methodTwo as params. Any sugestions?
There are several issues in your code.
Firstly, you want to modify the field you pass in as a parameter (as suggested by result = res. You can't pass in a reference to a field, but you can pass in the field name, and use indexing to change the field. keyof T will allow you to pass in the field in a type safe way.
Secondly if you want to access a method on a service. Again we can do this passing in the method name, and we can constrain the service to have a method with the passed in method name, that returns an Observable. The result of the Observable can also be constrained to be of the same type of the field we are going to assign it to in order for the method to be fully type safe.
declare class Service1 {
method1() : Observable<number>
}
declare class Service2 {
method2() : Observable<string>
}
class MyClass {
resultOne!: number;
resultTwo!: string;
constructor() {
this.genericFunction(new Service1(), "method1", "resultOne");
this.genericFunction(new Service2(), "method2", "resultTwo");
this.genericFunction(new Service1(), "method1", "resultTwo"); // error resultTwo is a string, the method return Observable<number>
this.genericFunction(new Service2(), "method", "resultTwo"); // error method does not exit on Service2
this.genericFunction(new Service2(), "method2", "resultTwo2"); // error field does not exist on type
}
genericFunction<MethodKey extends string, ResultKey extends keyof MyClass>(service:Record<MethodKey, ()=> Observable<MyClass[ResultKey]>>, method:MethodKey, result: ResultKey){
service[method]().subscribe(
res => this[result] = res,
err => {}
);
}
}
Note We could have also passed in the function as a function not just as a name, but directly a typed function. The disadvantage of this is that we either have to use bind to ensure the service method will still have the correct this when it's called, or use an arrow function when calling (again to ensure the service method has the correct this). This is error prone though, bind results in an untyped function, so we can't check compatibility to the field, and someone might pass service.method directly and no error would be reported until runtime:
class MyClass {
resultOne!: number;
resultTwo!: string;
constructor() {
var service1 = new Service1()
var service2 = new Service2()
this.genericFunction(()=> service1.method1(), "resultOne");
this.genericFunction(()=> service2.method2(), "resultTwo");
this.genericFunction(service2.method2, "resultTwo"); // no error, depending on the implementation of method2 it might or might not work
this.genericFunction(service2.method2.bind(service2), "resultOne"); // no error, the service call will work, but we store it in an incompatible variable
this.genericFunction(()=> service1.method1(), "resultTwo");// error resultTwo is a string, the method return Observable<number>
this.genericFunction(()=> service2.method2(), "resultTwo2");// // error field does not exist on type
}
genericFunction<MethodKey extends string, ResultKey extends keyof MyClass>(method:()=> Observable<MyClass[ResultKey]>, result: ResultKey){
method().subscribe(
res => this[result] = res,
err => {}
);
}
}
try by using the following code:
private firstFunction () {
let response= genericFunction(this.serviceOne.methodOne())
}
private secondFunction () {
let response = genericFunction(this.serviceTwo.methodTwo())
}
Modify you Generic Function by just receiving a variable.
//if it is angular 4 or less
genericFunction (method: Observable) {
return method.map(res => {
return res.json();
});
}
//if it is angular 5 or 6
genericFunction (method: Observable) {
return method.pipe(
map(res => {
return res;
}));
}
I'm using javascript es-06 syntax in react-native for developing mobile apps. Here is my code:
Super class:
export class ApiCallback {
onResponse = (responseJson) => {
console.log("super response")
}
onError = (error) => {
console.log("super error")
}
}
Base class:
export class LoginCallback extends ApiCallback {
onResponse(responseJson) {
super.onResponse(responseJson)
console.log(responseJson)
}
onError(error) {
super.onError()
console.error(error)
}
}
Usage:
export class AuthenticationInteractor {
doLogIn(loginCallback: LoginCallback) {
fetch("http://google.com",
{
method: "GET"
})
.then((responseJson) => {
loginCallback.onResponse(responseJson)
})
.catch((error) => {
loginCallback.onError(error)
})
}
}
and
new AuthenticationInteractor().doLogIn(new LoginCallback())
Here, instead of calling base class method (which is printing all resonse json in onResponse()), it's calling parent class's onResponse() function and printing
"super response"
as result from base class's onResponse() function.
The simple answer is: you shouldn't use arrow functions as methods in classes.
export class ApiCallback {
onResponse(responseJson) {
console.log("super response")
}
onError(error) {
console.log("super error")
}
}
The complex answer is: when you declare class method as arrow function it won't be added to class prototype but will be added to object on initialization. In your case you add LoginCallback methods to prototype, but on initialization they were overwritten by methods from parent class.
So it's better to use classic functions as class methods always. But don't forget to bind context to them.
Please see below snippet, it will improve the understanding of function definition.
I think the abc = function(){}'s priority is bigger than function abc(){}.
So to replace the function in derived class you have to define the function in same way.
So in your case use
onResponse(responseJson) { console.log("superresponse")}
on parent class will solve the problem.
abc = function(){
console.log("abc");
}
function abc(){
console.log("def");
}
abc();
function def(){
console.log("abc");
}
function def(){
console.log("def");
}
def();
var ghi =function(){
console.log("abc");
}
var ghi = function(){
console.log("def");
}
ghi();