Can I use a function expression as class method in javascript? - javascript

I am looking forward about a concrete question in javascript.
I would like to use a function expression as a class method, but I don´t know if it is possible. I try to look any documentation about it but I didn´t find... This is an example:
class TourIdentifier extends HttpClient {
constructor(baseURL, langCode, ...attr) {
super({
baseURL,
headers: {
lang: langCode
}
});
this.model.init(...attr);
}
model = function init(...attr) {
this.tours_identifiers_identifier = attr[0];
this.tours_id = attr[1];
this.lang_code = attr[2];
this.tours_id_ = attr[3];
this.tours_identifiers_id = attr[4];
}
...
I know there are many ways to do this, but I just have this question, and I hope that anyone with more experience than me in JS can share they knowledge.
I appreciate your comments!
Best regards!

This is everything you need to know about method definition in classes MDN, but here is some of my explanation:
Basically you are trying to define a method in a class(which requires giving this method a name) and then assign a named function to it.
This will work, but you won't be able to call the method of the class using the function's name, but you will be able to do it using the method's name.
Imagine you have a object and a separate function:
function func() {return 'Hello'}
const obj = {};
And you want to assign the function to one of the object's properties you will do:
obj.myFunction = func;
And from now on you are going to call the function from the object like this:
obj.myFunction();
But of course we can assign the function at the object's declaration:
function func() {return 'Hello'};
const obj = {
myFunction: func
}
And you are still going to call the function form the object like this:
obj.myFunction();
And of corpse you can declare the named functioning in the object's declaration:
const obj = {
myFunction: function func() {return 'Hello'}
}
And you are still going to call the function form the object like this:
obj.myFunction();
So I hope you see where I'm going.... the same thing can be applied to classes, as we know they are just sugar syntax :)
In short this is possible, but the name of the function won't be useful.
class foo {
constructor() {
//something
}
method = function init() {
console.log('init 1');
}
}
const fooInstance = new foo();
fooInstance.method();

Related

JavaScript Function Get Object

Is there a way for a function to get the object value before the function name? Below is an example of what I am kinda trying to achieve. I want to be able to read the myElement variable within the function but not pass it as a parameter but pass it before the function with the dot.
Any simple examples or explanations would be most helpful.
var myElement = document.getElementById('myID');
myElement.myFunction();
function myFunction() {
alert(myElement);
}
The only way you could do this is to add myFunction to HTMLElements prototype (which is what gets returned by document.getElementById(). That's usually frowned upon, but if it's your own project and you know what you do, you could do that.
var myElement = document.getElementById('myID');
HTMLElement.prototype.myFunction = function() {
console.log(this);
}
myElement.myFunction();
<div id="myID"></div>
With this prototype in place, you can call myFunction on every HTMLElement in your code.
In regards to your last comment, the function could be
HTMLElement.prototype.myFunction = function() {
alert(this.id);
}
I don't see why you should do it, as it's much easier to just do
alert(myElement.id);
In regards to the comments, here's what I'd do. Instead of extending anything, create your own class (or function), that takes a HTMLElement. Now on this class, you can add whatever method you want, manipulate your element and then return the plain HTMLElement from a getter. You can obviously change that to whatever return you want.
class MyHtmlElement {
constructor(htmlElement) {
this._htmlElement = htmlElement;
}
alertId() {
alert(this._htmlElement.id);
// optional
return this;
}
logId() {
console.log(this._htmlElement.id);
// optional
return this;
}
setId(newId) {
this.htmlElement.id = newId;
// optional
return this;
}
setStyle(prop, val) {
this._htmlElement.style[prop] = val;
// optional
return this;
}
get htmlElement() {
return this._htmlElement;
}
set htmlElement(value) {
this._htmlElement = value;
}
}
const el = new MyHtmlElement(document.getElementById('foo'));
el
.setId('bar')
.logId()
.alertId()
.setStyle('background-color', 'red')
.setStyle('width', '100vw')
.setStyle('height', '100vh');
// If you need the plain element, return it
const plainHTMLElement = el.htmlElement;
console.log(plainHTMLElement);
<div id="foo"></div>
When a function is stored in an object and then called with theObject.theFunction(), the value of this within the function will be theObject.
function sayHello() {
alert('Hello, my name is ' + this.name);
}
let myObject = { name: 'Bob', speak: sayHello };
myObject.speak(); // shows the message 'Hello, my name is Bob'
Now if you want to be able to create your own function and let it be used by an Element, you either need to store the function in the Element instance first or to add it to the Element prototype, both of which I highly discourage. If you feel like you have to do this, there's a flaw in your design.
Still, if you have a good reason to add a custom method to an existing object, I recommend you look up lessons about prototype inheritance in JavaScript, or read my old answer about it here if you're not sure how it works. You could say, make a function which adds methods to an object when it is called, like this:
function addMethods(elem) {
elem.speak = sayHello;
}
let myElement = document.getElementById('myID');
addMethods(myElement);
myElement.speak(); // Hello, my name is <value of the element's name attribute>
Or you could add the method to the prototype of all elements:
Element.prototype.speak = sayHello;
let myElement = document.getElementById('myID');
myElement.speak();
While browsers have let people do this since forever ago, there is technically no guarantee that Element is publicly available, or that its prototype is modifiable, or that you can add methods to Element instances. The Prototype framework (an inconveniently named third party library) has been using these techniques for a long time, but it did cause them a couple issues. jQuery prefers using a different approach, wrapping elements in another object on which custom methods are put.

Sinon - spy on constructor method

I've found a couple of related questions but none seem to help with what I want to implement.
So, I would like to spy on a constructor method so that when an object created with the constructor calls this method within a different scope another function, I can know the arguments with which that call was made.
Example:
function Constructor(args){
this.method = sinon.spy()
}
function someFunction(){
obj = new Constructor(args);
obj.method()
}
console.log(Constructor.method.args[0]); // list the args of the obj.method() call
Any help will be much appreciated.
Edit: I realized I phrased the question wrong and ended up asking something utterly trivial :-)
This way, you can spy over Constructor.method :
function Constructor(args){
this.method = function() {}
}
const obj = new Constructor();
obj.method = sinon.spy(obj.method);
obj.method('someArg');
console.log(obj.method.args[0]); // [ 'someArg' ]
But doing as you say is impossible, you cannot have a static method and a class method with the same name, and also what about if you instantiate that class more than once... Anyway the best I can come with is a solution with Proxy on constructor, like that :
function Constructor(args) {
this.method = function () {}
}
const ProxyConstructor = new Proxy(Constructor, {
construct: function (target, args, newTarget) {
const c = new target(...args);
const origMethod = c.method;
c.method = function (...args) {
ProxyConstructor.methodArgs = ProxyConstructor.methodArgs || [];
ProxyConstructor.methodArgs = ProxyConstructor.methodArgs.concat(args)
origMethod(...args);
};
return c;
}
});
function someFunction() {
obj = new ProxyConstructor();
obj.method('test')
}
someFunction();
console.log(ProxyConstructor.methodArgs); // ['test']
You can paste that code in a file and try it. Also, sometime when writing tests, you might have to refactor your code to make it testable, or you could start by writing the tests before writing your code (TDD).

TypeScript call function in dynamic (anonymous) function

I am trying to create a dynamic function in TypeScript which calls an already existing function like:
let dynamicFunction = new Function("existingFunction(\"asdf\");");
function existingFunction(name: string) {
console.log(name);
}
While debugging in chrome dynamicFunction looks like this:
(function() {
existingFunction("asdf");
})
When I try to execute dynamicFunction, it says "Uncaught ReferenceError: existingFunction is not defined", which is no surprise because it's a different scope, but how can I actually call exisitingFunction inside dynamicFunction?
Any help would be greatly appreciated!
Edit:
to be more precise: I've got a typescript file which contains one module.
This module exports a function which should return the created dynamic function.
The created dynamicFunction is then used in another module which actually contains the exisitingFunction.
I've chosen this approach because I need to convert a given string to an executable condition, which will be executed many times.
For example: convert string "VALUE==1" to:
function () {
return exisitingFunction("VALUE") == 1;
}
A short example of how it should look like:
parser.ts:
export module Parser {
export function getFunction(expression: string) {
// Calculating condition...
let condition = "existingFunction(\"VALUE\") == 1;"
return new Function(condition);
}
}
condition.ts:
import { Parser } from "./parser";
class Condition {
// getting the DynamicFunction
private _dynamicFunction = Parser.getFunction("VALUE==1");
someFunctionInsideCondition() {
// Calling the DynamicFunction
this._dynamicFunction();
}
}
// Maybe this function should be somewhere else?
function existingFunction(name: string) {
console.log(name);
return 1;
}
I hope this explains my problem a little bit better.
From the Function documentation
Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.
so you'll have to pass existingFunction as an argument or define it in the global space.
try with
var existingFunction = function(name: string) {
console.log(name);
}
Also have a look at eval which will give you access to the current scope ...
--- Update
After the question update and considering your comment about not wanting to use eval because of security concerns (with which i totally agree)
The problem is that in the generated function's scope, this is undefined. Making your existingFunction part of the global scope is already a bad idea and between Typescript and the modules architecture doesn't seem possible at all.
So why not passing a context to the generated function?
This will allow you to control how much of your application to expose to the generated function, while giving it access to external methods.
Something along the lines of:
class Parser {
static getFunction(expression) {
let condition = new Function("context", "context.existingFunction(\"VALUE\") == 1;");
return condition;
}
}
class Condition {
constructor() {
this._dynamicFunction = Parser.getFunction("VALUE==1");
}
someFunctionInsideCondition() {
// Calling the DynamicFunction
this._dynamicFunction(this);
}
existingFunction(name) {
console.log("hello " + name);
return 1;
};
}
let c = new Condition();
c.someFunctionInsideCondition();
Of course your context can be a different object instead of this, where you keep all your utility functions.
I had to donwpile (compile it down, my own word) to es2015 to make the example run here, but I made it originally in Typescript and works fine
I would skip the usage of new Function and instead do it as follows.
The parser.ts file would contain this:
export class FunctionGenerator {
constructor(private fn: Function) {}
makeFunction(args: string): Function {
const [variable, val] = args.split("==");
return () => this.fn(variable) == val;
}
}
This is basically a factory that allows creating a series of functions that call the function passed when the factory is created. You can then use makeFunction for the specific checks you want to perform. (Note that I used == like in your question. I much prefer using === unless there's a reason against it.)
It can then be used like this:
import * as parser from "./parser";
let vars = {};
// This is a simulation of your funciton. It just plucks values from `vars`.
function existingFunction(name: string) {
return vars[name];
}
function resetVars() {
vars = {
"VALUE": 1,
"FOO": 2,
"BAR": 3,
};
}
function test(gen) {
const fn1 = gen.makeFunction("VALUE==1");
console.log(fn1(), "should be true");
const fn2 = gen.makeFunction("BAR==3");
console.log(fn2(), "should be true");
vars["BAR"] = 7;
// Call the same function again, but with a new value in `vars`.
console.log(fn2(), "should be false");
const fn3 = gen.makeFunction("BAR==1000");
console.log(fn3(), "should be false");
}
resetVars();
const gen = new parser.FunctionGenerator(existingFunction);
test(gen);

Using a constructor on a JavaScript static instance

I'm writing a JavaScript library in which I want some methods and properties public and other private. The following seems a great way of doing this whilst wrapping everything up into a single object.
(function (window) {
var Thing = function() {
// private var
var variable = "value";
return {
// public method
method:function() {
alert(variable);
}
}
}();
window.Thing = Thing;
})(window);
Thing.method();
Which is great. (Mostly grabbed from here).
However, I'd still like to be able to use the constructor of Thing to pass in some arguments.
Is there anyway I can provide a constructor in the return statement, or use prototype to override the constructor? So I can call:
Thing(stuff);
Right now, if I try that it causes:
Uncaught TypeError: Property 'Thing' of object [object DOMWindow] is not a function
Which makes sense as it's not returning itself, but ideally it'd be possible to call a constructor.
OR, is this just baaaad and I should steer clear of some or all of this?
To accomplish what you are asking, do something like this:
(function (window) {
var thingMaker= function(stuff) {
// private var
var variable = "value";
return {
// public method
method:function() {
alert(variable);
}
alertStuff:function() {
alert(stuff);
}
}
};
window.thingMaker= thingMaker;
})(window);
var myThing = window.thingMaker(stuff);
myThing.alertStuff()
More information can be found by searching the googlenets for Douglas Crockford. Some great and very informative videos by him are available on yui theater. But I would have to ask, why create another framework when there are already so many great ones out there (jquery,prototype,yui,dojo to name a few)
Thing is already created, so you are always going to be too late to call a 'constructor'.
You could pass variables in like this:
(function (window, var1, var2) {
var Thing = function() {
// private var
var variable = "value";
return {
// public method
method:function() {
alert(variable);
}
}
}();
window.Thing = Thing;
})(window, var1, var2);
Thing is an Object with one method called method:
{
// public method
method:function() {
alert(variable);
}
}
Thing.method(); // alerts "value"
You could return instead:
function () {
alert(arguments)
}
Then
Thing(6,5,4); // alerts 6,5,4

JsMockito stubbing a field

I have a simple object that I am mocking, assume something like this:
function MyObject() {
this.SomeVariable = "some-value";
this.SomeFunction = function() { ... };
}
Now I want to mock the variable, which I was doing:
var mockedObject = mock(MyObject);
when(mockedObject).SomeVariable.thenReturn("some-other-value");
However it tells me that I cannot use thenReturn() on this... cant remember the exact error but does anyone know a way around this? Or should I just do:
var mockedObject = mock(MyObject);
mockedObject.SomeVariable = "some-other-value";
You can only stub or verify interactions (function calls). Because "SomeVariable" is an attribute in the object, not a function in the object you cannot stub or verify uses of it.
So your conclusion is correct; you should simply provide the value you want as an attribute of the object:
var mockedObject = mock(MyObject);
mockedObject.SomeVariable = "some-other-value";
An alternative, if it's your own code, is to change the original object to use a "getter" style function instead, e.g.:
function MyObject() {
this.SomeVariable = function() { return "some-value"; };
this.SomeFunction = function() { ... };
}
For true "objects" this is often a better approach than using attributes.

Categories