Sinon - spy on constructor method - javascript

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).

Related

Can I use a function expression as class method in 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();

JS run function from constructor after object instantiation

Is it possible to do this:
var hammer = new Hammer(); // create a new instance
hammer(nail); // really call Hammer.prototoype.hit(object);
I can figure it out on a raw object, but not when creating a new instance of an object. This is what I am running into:
function Hammer(options) {
this.config = options.blah;
this.hit(/* ? */);
return this;
}
Hammer.prototype.hit = function(obj) {
// ...
}
When I call the constructor, I want to pass in special options - not what nail to hit. However, when I call it later, I want to pass in a nail. I'm missing something.
One solution is to not create a constructor function at all:
var hammer = newHammer();
hammer(nail);
hammer.clean();
function newHammer(options) {
var config = options.blah;
hit.clean = clean;
return hit;
function hit(obj) {
// ...
}
function clean() {
// ...
}
}
To me, this is a much cleaner solution than messing around with constructors and prototypes.

Is there a good pattern for JavaScript calling overridden functions in a parent without knowing the specific parent?

Basically I want inheritable functions as in
Base = function() { };
Base.prototype.foo = function() {
console.log("base foo");
};
Derived = function() { };
somelib.inherit(Derived, Base);
Derived.prototype.foo = function() {
console.log("derived foo");
}
d = new Derived():
d.foo();
And I want it to print
derived foo
base foo
Yes I know I can explicitly call Base.prototype.foo.call(this); I'm just wondering if there is a pattern for calling overridden super class functions automatically. The problem I'm trying to solve is 2 fold.
derived classes should NOT have to remember to call their parent's method, it just happens automatically.
if 1. can't happen then at least I'd like Derived not to call Base by name since that's brittle. Rather I'd like it call parentclass or something so you don't have to know the base. That way if you change the name of the base you don't have to go fixing every derived class.
You can implement such functionality by using a structure like:
function Base(){}
Base.prototype.destroy = function(){console.log('Base destroy');};
function Derived(){}
Derived.prototype = new Base; // Let Derived inherit from Base
// Override the `destroy` method
Derived.prototype.destroy = function() {
console.log('Derived destroy');
// Call parent class method
this.constructor.prototype.destroy();
// If the context of the method is important, you can use Function.call:
//this.constructor.prototype.destroy.call(this);
};
// Create an instance of Derived, and call the destroy method:
(new Derived).destroy();
I would suggest thinking about exactly why you are doing this, at least in terms of requirement #1. Keep in mind that your desired pattern would take away a great deal of flexibility. For instance, if you have a situation where you want to print the statements in the opposite order:
base foo
derived foo
You would either have to abandon your pattern or create a function foo2() in the derived class which then calls foo() in the base class. Neither is very pretty.
Same goes if you even want to do something as simple as:
derived foo
base foo
one more thing in the derived function
I would contend that using this pattern may work for the exact thing you want to do right now, but may give you fits when you want to make a seemingly trivial change down the road. All to save one line of code!
As far as I know there is no language integrated destructor functionality in JavaScript. It is all about frameworks. If you are using ASP.NET Ajax, for example, the framework would expect that your objects would have a dispose method, responsible for freeing up resources (event handlers). So, it is up to you.
Ok, this isn't quite what you are looking for, in that it's not a "pattern", but it is a potential implementation path you could follow:
Take a look # the MooTools Class.Extras package (for lack of a better word). Using the Chain Class, you could probably get the desired functionality.
var parent = (function () {
var construct = function () {
};
construct.prototype = {
constructor: construct,
destroy: function () {
console.log('parent destruction');
}
}
return construct;
})();
var child = (function (parent) {
var construct = function () {
};
construct.prototype = Object.create(parent.prototype);
construct.prototype.constructor = construct;
construct.prototype.destroy = function () {
parent.prototype.destroy.call(this); // calling parent, too
console.log('child destruction');
};
return construct;
})(parent);
child_instance = new child();
child_instance.destroy();
I would prefer a way where I don't assign Derived = chainify() so that the api would be the same as you had in your question but as of right now this is the best way I can get it to work. It works by replacing each method of the object with a method that calls the replaced method and travels up the parent chain calling their methods along the way.
function chainify() {
return function () {
var property;
for (property in this) {
if (typeof this[property] === "function") {
this[property] = chain(this[property], property);
}
}
function chain(method, method_name) {
return function() {
method();
var current = this;
while (current = current.parent) {
if (current.hasOwnProperty(method_name)) {
current[method_name].apply(this, arguments);
}
}
};
}
}
}
var somelib = function() { };
somelib.inherit = function (derive, base) {
derive.prototype = new base;
derive.prototype.parent = base.prototype;
};
var Base = function() { };
Base.prototype.foo = function() {
console.log("base foo");
};
var Derived = chainify();
somelib.inherit(Derived, Base);
Derived.prototype.foo = function() {
console.log("derived foo");
};
d = new Derived();
d.foo();

JavaScript: How to create a new instance of a class without using the new keyword?

I think the following code will make the question clear.
// My class
var Class = function() { console.log("Constructor"); };
Class.prototype = { method: function() { console.log("Method");} }
// Creating an instance with new
var object1 = new Class();
object1.method();
console.log("New returned", object1);
// How to write a factory which can't use the new keyword?
function factory(clazz) {
// Assume this function can't see "Class", but only sees its parameter "clazz".
return clazz.call(); // Calls the constructor, but no new object is created
return clazz.new(); // Doesn't work because there is new() method
};
var object2 = factory(Class);
object2.method();
console.log("Factory returned", object2);
A simpler, cleaner way with no "factories"
function Person(name) {
if (!(this instanceof Person)) return new Person(name);
this.name = name;
}
var p1 = new Person('Fred');
var p2 = Person('Barney');
p1 instanceof Person //=> true
p2 instanceof Person //=> true
Doesn't this work?
function factory(class_, ...arg) {
return new class_(...arg);
}
I don't understand why you can't use new.
If you really don't want to use the new keyword, and you don't mind only supporting Firefox, you can set the prototype yourself. There's not really any point to this though, since you can just use Dave Hinton's answer.
// This is essentially what the new keyword does
function factory(clazz) {
var obj = {};
obj.__proto__ = clazz.prototype;
var result = clazz.call(obj);
return (typeof result !== 'undefined') ? result : obj;
};
I guess browser independent solution would be better
function empty() {}
function factory(clazz /*, some more arguments for constructor */) {
empty.prototype = clazz.prototype;
var obj = new empty();
clazz.apply(obj, Array.prototype.slice.call(arguments, 1));
return obj;
}
Because JavaScript doesn't have classes, let me reword your question: How to create a new object based on an existing object without using the new keyword?
Here is a method that doesn't use "new". It's not strictly a "new instance of" but it's the only way I could think of that doesn't use "new" (and doesn't use any ECMAScript 5 features).
//a very basic version that doesn't use 'new'
function factory(clazz) {
var o = {};
for (var prop in clazz) {
o[prop] = clazz[prop];
}
return o;
};
//test
var clazz = { prop1: "hello clazz" };
var testObj1 = factory(clazz);
console.log(testObj1.prop1); //"hello clazz"
You could get fancy and set the prototype, but then you get into cross-browser issues and I'm trying to keep this simple. Also you may want to use "hasOwnProperty" to filter which properties you add to the new object.
There are other ways that use "new" but sort of hide it. Here is one that borrows from the Object.create function in JavaScript: The Good Parts by Douglas Crockford:
//Another version the does use 'new' but in a limited sense
function factory(clazz) {
var F = function() {};
F.prototype = clazz;
return new F();
};
//Test
var orig = { prop1: "hello orig" };
var testObj2 = factory(orig);
console.log(testObj2.prop1); //"hello orig"
EcmaScript 5 has the Object.create method which will do this much better but is only supported in newer browsers (e.g., IE9, FF4), but you can use a polyfill (something that fills in the cracks), such as ES5 Shim, to get an implementation for older browsers. (See John Resig's article on new ES5 features including Object.create).
In ES5 you can do it like this:
//using Object.create - doesn't use "new"
var baseObj = { prop1: "hello base" };
var testObj3 = Object.create(baseObj);
console.log(testObj3.prop1);
I hope that helps
Another way:
var factory = function(clazz /*, arguments*/) {
var args = [].slice.call(arguments, 1);
return new function() {
clazz.apply(this, args)
}
}
To answer the question more literally, i.e. how to have myClass() return new myClass()... It's not possible, and here's why...
You'd have to do it like this, to make sure that the class name exists and that you're capturing calls to myClass() (using apply functionality, from Proxy/trap/handler-land):
class A {
}
A.prototype.apply = function() {
return new A();
}
A(); //Error occurs here.
OR:
class B {
}
B.apply = function() {
return new B();
}
B(); //Error occurs here.
And the reason this doesn't work is the output when you try to evaluate either of the above: Uncaught TypeError: class constructors must be invoked with 'new'
Thus, JavaScript literally does not allow it, because you have declared it to be a class type. However, you can of course have a separately named function that creates a new instance for you, like in the answer above, or more simply:
class A {
}
//Note: a != A.
function a() {
return new A();
}
OR, another way to approach this problem is to not use a class, per-se, but a function or a regular JS object {}, like in the older days of JavaScript. Multiple other answers show how to do this.
What you could also do is use eval.
Of course there are security concerns with eval, but is it really different to any other dynamic instanciation?
await import("/path/to/module") //use this to dynamically load module if you like
let obj = `eval new ${classname}();`

Call a JavaScript function name using a string?

How can I hook up an event to a function name I have defined as a string?
I'm using Prototype.js, although this is not Prototype-speficic.
$(inputId).observe('click', formData.fields[x].onclick);
This would result in JavaScript complaining that my handler is not a function. I would prefer not us use eval().
Property accessors can be used to access any object's properties or functions.
If the function is in the global scope, you can get it using the window object:
var myFunc = window[myFuncName];
This also works within the this scope:
var myFunc = this[myFuncName];
I have worked on this problem, as I needed a function like this. Here is my sandbox code, not thoroughly tested, but can be a startpoint for others.
Note that there is one eval() in the code as I couldn't figure out how to bypass that step, maybe a javascript quirk and cannot be done in any other way. Let me know if there is a way to get rid of eval() here!
executeFunctionByName = function(functionName)
{
var args = Array.prototype.slice.call(arguments).splice(1);
//debug
console.log('args:', args);
var namespaces = functionName.split(".");
//debug
console.log('namespaces:', namespaces);
var func = namespaces.pop();
//debug
console.log('func:', func);
ns = namespaces.join('.');
//debug
console.log('namespace:', ns);
if(ns == '')
{
ns = 'window';
}
ns = eval(ns);
//debug
console.log('evaled namespace:', ns);
return ns[func].apply(ns, args);
}
core = {
paragraph: {
titlebar: {
user: "ddd",
getUser: function(name)
{
this.user = name;
return this.user;
}
}
}
}
var testf = function()
{
alert('dkdkdkd');
}
var x = executeFunctionByName('core.paragraph.titlebar.getUser', 'Ikon');
executeFunctionByName('testf');
... or this[myFuncName];
Perhaps?
setTimeout ( "myFunc()", 1 );
Just an eval would do the job
var call = eval("method_name").call(args);
Looks like formData.fields[x].onclick holds the name of a global function? If so try:
$(inputId).observe('click', window[formData.fields[x].onclick]);
window.myFunction === window["myFunction"]
Do you know what the onclick property contains or what type it is? I assume this is prototype specific stuff, as "fields" does not exist in DOM forms.
If you need to call a string function with arguments, do this:
window[stringFunctionName].apply( window, arrayOfArguments )
You can use scope in place of window if preferred
update:---
use ES6 export and import
a.js
const fn = {
aaa: function() {
//code
},
bbb: function() {
//code
},
//codes ....
nnn: function() {
//code
}
}
export default fn
b.js
import someFn from './a'
//eg
const str1='aaa'
const str2 = 'bbb'
someFn[str1]()
eval('str') (obsolete feature https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features )
setTimeout('str') setInterval('str')
window['str'] (but...sometimes,global object is not window)
new Function('str')
These methods above always not be recommend by some reasons, but they are really convenient to use.
These methods below are safe, but really not conveninet to use.
switch...case (or if...else)
switch(str){
case 'str1':
fn1()
break
case 'str2':
fn2
//and so on
}
put functions in a object
const fn={
str1:fn1,
str2:fn2
//and so on
}
fn[str1] //call function

Categories