I have a function like:
define(['module', 'controller'], function(module, controller){
(new module).build();
});
inside module.build I want to get the arguments automatically of the parent like:
module = function(){
this.build = function(args){
// make args the arguments from caller ( define ) fn above
};
};
I know I could do something like:
module.build.apply(this, arguments);
but I was wondering if there was a better way. Any thoughts?
There is a way to do this, illustrated in this example (http://jsfiddle.net/zqwhmo7j/):
function one(){
two()
}
function two(){
console.log(two.caller.arguments)
}
one('dude')
It is non-standard however and may not work in all browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
You would have to change your function like so:
module = function(){
this.build = function build(args){
// make args the arguments from caller ( define ) fn above
console.log(build.caller.arguments)
};
};
Related
I'm testing my app, I have a dependency injection system that works like this (I paste here a very simplified version)
test.start = function(callback) {
// do stuff;
load(callback);
}
function load(callback) {
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
// test.js
test.start(function(moduleA, moduleB) {
moduleA.functionA();
moduleB.functionB();
});
What I need to do is an async job before calling the callback and start the test, in this particular case, that would be
test.start = function(callback) {
// do stuff;
load(function(moduleA, moduleB, moduleC) {
moduleC.resetData(function() {
return callback(moduleA, moduleB);
})
});
}
I need to make this dynamic, for all the test that have different modules as their callback arguments.
Requirements are
I don't want to change test definitions
I must call load only once per test
So basically I need to create the middle function that add moduleC argument, run the resetData function and call the original callback.
I can build the dynamically extended argument list using
var fn = new Function(newArgumentList, /*function body*/) but I can't attach the correct function body (using a string) because I lose the original callback context.
UPDATE
The DI library is modularity and it reads the callback definition to grab dependency. So basically, given the callback function(foo, bar) { ... } I need to create a new function(foo, bar, db) { db.resetData(function() { return callback(foo, bar) } )}
If load, itself, is something you can change (e.g., not part of the test definitions), you'll be glad to know this is quite easy to do: You use Function#apply:
function load(callback) {
var origCallback;
if (/*flag saying you need to do this*/) {
origCallback = callback;
callback = function() {
/*...do your injected work here...*/
return origCallback.apply(this, arguments); // `arguments` looks like pseudo-code, but it isn't; it's an identifier created in the function's scope (for non-arrow functions)
};
}
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
You can even do it if you can't change load, provided you can update the load symbol to make it point at a new function:
var oldload = load;
load = function() {
/*...do your reset work here...*/
return oldload.apply(this, arguments);
};
If you're curious, yes, you can update the symbols created by function declarations (and yes, that's per spec, not "tricky"). E.g., this is perfectly valid:
function foo() {
console.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
console.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
Example:
function foo() {
snippet.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
snippet.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I found out the solution at the end.
Since modularity supports angular-like injection like this
['modules/a', 'modules/b', function(moduleA, moduleB) {
// use modules here
}]
I could write my custom callback like this
var customCallback = function() {
var args = Array.prototype.slice.call(arguments);
var injectedDBModule = args.pop();
injectedDBModule.resetData(function() {
// call the original callback function
return originalCallback.apply(null, args);
}
}
var loadCallback = originalInjectedModules; // ['moduleA', 'moduleB']
loadCallback.push('db');
loadCallback.push(customCallback);
modularity.load(loadCallback);
I am trying to create a class in JavaScript that would be inherited and than some of its methods would be overridden.
I am using the following structure :
function MyClass()
{
this.myvar = null;
this.someotherVar = { someFunction: function(a,b){ function1(a,b); })
}
// some functions
MyClass.prototype.functionA = function() {..}
MyClass.prototype.functionB = function() {functionC();}
//some functions that would be overrided
MyClass.prototype.function1= function() {...}
MyClass.prototype.functionC= function() {...}
Now I have 2 problems:
Might functionC be a problem to use in functionB because it is defined afterwards?
How can I relate to function1 inside someFunction in the right way?
1. Might functionC be a problem to use in functionB because it is defined afterwards?
No. You just have to call it properly:
function() { this.functionC(); }
// ^^^^^
otherwise the function won't be found.
Of course you also have to make sure that functionB is only called once functionC is defined.
2. How can I relate to function1 inside someFunction in the right way?
A tiny problem is that someFunction is not a method of the instance but of another object. To still call the correct function1, you can store a reference to the instance in a variable:
function MyClass() {
var self = this;
this.myvar = null;
this.someotherVar = {
someFunction: function(a,b){
self.function1(a,b);
}
};
}
How to write chainable functions but do not pollute $.fn ? Write functions only for using inside my plugin. Is it possible?
$('.myclass').makeSomething().andOneMoreFunction().andLast();
It is correct approach?
UPD.
The best solution in my case is extension method:
String.prototype.getMyLength = function(){return this.length;}
And now I can apply this function to any string like this:
var mystring = "test";
mystring.getMyLength();
Or
"teststring".getMyLength()
And make it chainable:
String.prototype.getMe = function(){return this;}
"string".getMe().getMe().getMe().getMe().getMe();
Thanks for answers!
You can chain all you want. If you define a $.fn yourself it is important that you return this at the end of you function.
If you want to write some javascript yourself you can also chain! It just depends on what you return. So if you return some other object, you can chain on from that object. The return value is used for this.
Example
var obj = {
test : function(){
alert("Y");
return this;
},
test2 : function(){
alert("2");
return this;
}
}
obj.test().test2(); // And so on since it returns this
jQuery Plugin API
$.fn.test = function(){
var methods = {
method0 : function(){
alert("method0");
return this;
}
};
return methods;
}
var api = $("obj").test(); // Returns methods
api.method0(); // Calling a function from the returned methods.
// OR
$("obj").test().method0();
Above function is not jQuery chainable anymore. So you can't use the $("obj").test().addClass("test") because you return your own API!
You can avoid pollution by using the first parameter of your plugin's function to specify the method of choice; for instance
(function () {
var o = { // object holding your methods
'bar': function () {console.log('bar', this); return this;},
'foobar': function () {console.log('foobar', this); return this;}
};
$.fn.foo = function (method /*, args*/) {
return o[method].apply(
this,
Array.prototype.slice.call(arguments, 1) // pass your args
);
};
}());
and then
$('something').foo('bar').foo('foobar');
/*
bar, thisobj
foobar, thisobj
*/
This way you keep access to the jQuery object as normal, too.
When you call a.foo(), the function foo is invoked with this set to a. You can use this to your advantage.
Also recall that the expression a.foo() evaluates to whatever you returnd from within the function.
So, just return this.
Then a.foo() evaluates back to a, and (a.foo()).bar() becomes equivalent to calling a.foo() then calling a.bar()... i.e. chained operations on a!
$.fn is not particularly magical — it simply uses the above logic in the same way that you are about to.
I found myself using a weird way to add callback functions to my functions and I was wondering if there is a more generic way to add callbacks to functions, best case I would have a situation where all of my functions check the last given param for being a function and if so use it as a callback.
This is how I did it in the past:
var myFunc = function( obj ) {
if ( arguments.length > 0 ) {
if ( _.util.typeofObj( arguments[arguments.length-1] ) === Function ) {
var callback = arguments[arguments.length-1];
}
}
// some code ...
if ( callback !== undefined ) {
callback();
}
};
var foo = myFunc( myObj, function(){
alert( 'Callback!' );
});
Any suggestions?
I prefer a formal parameter:
var myFunc = function(obj, callback) {
...
}
This way it makes it obvious that there is a callback. You also don't have to mess around with the arguments object; you can just check to see if callback is undefined, and then check to see if it is of the appropriate type (Function).
You could, if you really want to, extend Function.prototype with a .cb prototype. Something like:
Function.prototype.cb = function(cb){
var self = this;
return function(){
self.callback = cb || function(){};
self.apply(self, arguments);
}
}
then your code would compress to:
var myFunc = function(obj){
// some code
this.callback(); // callback will always be defined
}
and the call would slightly change:
myFunc.cb(function(){
// callback code
})(myObj);
Just an idea. You can make the syntax pretty much whatever you want.
Not sure what you are trying to do here, but a way to add a callback that seems more straight forward:
function( obj, callback ) {
if(callback) {
callback();
}
}
I'm playing around with Function.prototype as a learning exercise and was trying to make a generic curry method that could be used like this:
// Any old function
var myFn = function(arg1, arg2) {
console.log(arg1, arg2);
};
// Curry some parameters and return a new function
var myFnCurry = myFn.curry("A");
// Call the curried function, passing in the remaining parameters
myFnCurry("B"); // Outputs: A B
It's fairly straight forward to implement this feature as a function rather than a method using the following approach:
var curry = function(fn) {
var slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function() {
fn.apply(this, args.concat(slice.call(arguments)));
};
};
// Example
var myFnCurry = curry(myFn, "A");
myFnCurry("B"); // Outputs: A B
However, I really wanted to be able to utilise the Function prototype, like this:
Function.prototype.curry = function() {
var slice = Array.prototype.slice,
args = slice.call(arguments);
return function() {
// If we call curry like this: myFn.curry()
// how can we get a reference myFn here?
???.apply(this, args.concat(slice.call(arguments)));
};
};
I'm not sure how to get a reference to the myFn function (denoted by the ??? above) that the curry method is being called from.
Is there a way to access the parent function in this circumstance?
Cheers,
Ian
You're calling curry in a context of your function object (myFn.curry), and therefore inside of curry this refers to that function. But your inner function will be called in the context of global object, that's why you need to store the reference to the outer function in a closure.
Function.prototype.curry = function() {
var self = this, args = [].slice.call(arguments)
return function() {
return self.apply(this, args.concat([].slice.call(arguments)));
}
}