Javascript Handling Calling Functions with Named Args and Normal - javascript

If I have the function:
function(foo, bar, baz);
And I want to allow for both named arguments and normal function calls, what is the best way of handling this? In php you can extract the variables into the local namespace but as far as I know the only way to handle this in javascript is by handling both scenarios separately. I've given a code example below:
function(foo, bar, baz)
{
if(typeof(foo) == 'object') // Named args
{
alert(foo.foo);
alert(foo.bar);
alert(foo.baz);
}
else
{
alert(foo);
alert(bar);
alert(baz);
}
}
myFunc('a', 'b', 'c');
myFunc({ foo: 'a', bar: 'b', baz: 'c' });
Any javascript gurus out there who can teach me the ways of javascriptFu?

Since you cannot access the local scope dynamically (without evil eval), you should consider the following approach:
var myFunc = function (foo, bar, baz) {
if (typeof(foo) === 'object') {
bar = foo.bar;
baz = foo.baz;
foo = foo.foo; // note: foo gets assigned after all other variables
}
alert(foo);
alert(bar);
alert(baz);
};
You simply translate the named args to regular variables manually. After that, your code will run for both cases without changes.

Do it with elegance:
var myFunc = (function (foo, bar, baz) {
// does whatever it is supposed to do
}).
withNamedArguments({foo:"default for foo", bar:"bar", baz:23 });
myFunc({foo:1}); // calls function(1, "bar", 23)
myFunc({}); // calls function("default for foo", "bar", 23);
myFunc({corrupt:1}); // calls function({corrupt:1})
myFunc([2,4], 1); //calls function([2,4], 1)
Even this one works
Array.prototype.slice =
Array.prototype.slice.withNamedArguments({start:0, length:undefined});
[1,2,3].slice({length:2}) //returns [1,2]
[1,2,3].slice(1,2) //returns [2,3]
... or here, parseInt()
parseInt = parseInt.withNamedArguments({str:undefined, base:10});
parseInt({str:"010"}); //returns 10
Just enhance the Function object:
Function.prototype.withNamedArguments = function( argumentList ) {
var actualFunction = this;
var idx=[];
var ids=[];
var argCount=0;
// construct index and ids lookup table
for ( var identifier in argumentList ){
idx[identifier] = argCount;
ids[argCount] = identifier;
argCount++;
}
return function( onlyArg ) {
var actualParams=[];
var namedArguments=false;
// determine call mode
if ( arguments.length == 1 && onlyArg instanceof Object ) {
namedArguments = true;
// assume named arguments at the moment
onlyArg = arguments[0];
for ( name in onlyArg )
if (name in argumentList ) {
actualParams[idx[name]] = onlyArg[name];
} else {
namedArguments = false;
break;
}
}
if ( namedArguments ) {
// fill in default values
for ( var i = 0; i < argCount; i++ ) {
if ( actualParams[i] === undefined )
actualParams[i] = argumentList[ids[i]];
}
} else
actualParams = arguments;
return actualFunction.apply( this, actualParams );
};
};

This is always awkward and not very rigourous but it's far safer to check the arguments for the absence of data than for a particular positive expectation, especially typeof on object.
Some variation on the below, the strategy here being to translate a DTO style input into a named argument style input (the opposite is also reasonable but I find less obvious). The advantage of this strategy is once you've passed this translation block, the rest of the code doesn't care how you got there.
// translate to named args - messy up front, cleaner to work with
function(foo, bar, baz)
{
// Opt 1: default to named arg, else try foo DTO
bar = (typeof(bar) != 'undefined' ? bar : foo.bar);
// Opt 2: default to named arg, else check if property of foo, else hard default (to null)
baz = (typeof(baz) != 'undefined' ? baz : typeof(foo.baz) != 'undefined' ? foo.baz : null);
// the first argument is always a problem to identify in itself
foo = (foo != null ? typeof(foo.foo) != 'undefined' ? foo.foo : foo : null);
}
// translate to object - cleaner up front, messier to work with
function(foo, bar, baz)
{
var input = (typeof(foo.foo) != 'undefined' ? foo : { 'foo' : foo, 'bar' : bar, 'baz' : baz });
}
The first arg (foo here) is always a problem because you expect it to be in one of two complex states (where the other args are always a single complex state or undefined) and you cannot process it until you've dealt with all the other args because obviously once you've changed it it's unreliable to use it for initialising anything else.

Related

How to check if a variable is defined if you have a string of it in javascript?

I know if you want to check if a variable a is defined you can do this
if (typeof a !== 'undefined') {
// the variable is defined
}
but what if you want to make a function out of it, like this
function checkDefined(name) {
return typeof name !== 'undefined';
}
checkDefined("a");
this wouldn't work, but how can I get it to work if I have to pass a string version of the variable name?
Thanks
Checking in global scope(window):
var a = 'test';
var b = function() {};
function checkDefined(name) {
return typeof this[name] !== 'undefined';
}
console.log(checkDefined("a"));
console.log(checkDefined("b"));
console.log(checkDefined("c"));
If you want check if variable or function is declared in class object you should pass new context to checkDefined method:
function MyCustomClass() {
this.c = 'test';
}
function checkDefined(name) {
return typeof this[name] !== 'undefined';
}
// Create new object of type MyCustomClass
var myCustomClassObject = new MyCustomClass();
// In this way you can check if variable/function is defined in object
console.log(checkDefined.apply(myCustomClassObject, ["a"]));
console.log(checkDefined.apply(myCustomClassObject, ["b"]));
console.log(checkDefined.apply(myCustomClassObject, ["c"]));
apply will call a function immediately letting you specify both the value of this and any arguments the function will receive
Inspired by this answer. I think you can try to return with eval:
function checkDefined(name) {
return eval("typeof " + name) !== 'undefined';
}
Example:
var a = 1;
checkDefined("a") // true
checkDefined(a) // true
checkDefined("b") // false
Local variables are properties of the currently scoped this object.
const log = output => document.querySelector('pre')
.innerText += output + '\n'
/* In this example, running in the browser, `this` points to `window`, but
in other environments would still point to whatever is the global object.
Bind `this` to the ``checkDefined` so to ensure it keeps the same value
as where you are calling from. */
const checkDefined = function chkDef(v) {
return typeof this[v] !== 'undefined'
}.bind(this)
a = 5
log(checkDefined('a'))
log(checkDefined('b'))
/* This is the same basic idea, but we make it a little more portable by
not binding until right before we use it, so `this` has the correct
scope. */
unboundCheckDefined = function chkDef(v) {
return typeof this[v] !== 'undefined'
}
newScope()
function newScope() {
c = 5
const checkDefined =
unboundCheckDefined.bind(this)
log(checkDefined('a'))
log(checkDefined('b'))
log(checkDefined('c'))
}
<pre></pre>
You should also pass on which object context you need to check if the variable is defined or not. If it's global pass the window object
function checkDefined(name, ref) {
if(!ref) {
ref = window;
}
return !!ref[name]
}
checkDefined("a"); //false if already not defined
var obj = { a: 1, b:2};
checkDefined("a",obj);//true

Test if variable is a specific bound function in using javascript's Function.prototype.bind()

I am trying to work out how to test if a variable is an instance of a specific bound function. Consider the following example:
var func = function( arg ) {
// code here
}
myFunc = func.bind( null, 'val' );
if( myFunc == func ) {
console.log( true );
} else {
console.log( false );
}
Unfortunately this results in false. Is there some sort of way of testing the variable to find out what function it is bound to?
No, there is not a way to do this. .bind() returns a new function that internally calls the original one. There is no interface on that new function to retrieve the original one.
Per the ECMAScript specification 15.3.4.5, the returned "bound" function will have internal properties for [[TargetFunction]], [[BoundThis]] and [[BoundArgs]], but those properties are not public.
If you tell us what higher level problem you're trying to solve, we might be able to come up with a different type of solution.
If you yourself control the .bind() operation, you could put the original function on the bound function as a property and you could test that property:
var func = function( arg ) {
// code here
}
myFunc = func.bind( null, 'val' );
myFunc.origFn = func;
if( myFunc === func || myFunc.origFn === func) {
console.log( true );
} else {
console.log( false );
}
Demo: http://jsfiddle.net/jfriend00/e2gq6n8y/
You could even make your own .bind() replacement that did this automatically.
function bind2(fn) {
// make copy of args and remove the fn argument
var args = Array.prototype.slice.call(arguments, 1);
var b = fn.bind.apply(fn, args);
b.origFn = fn;
return b;
}
You can't do this directly because functions, just like Objects, have their equality tested by their reference which no longer matches, §11.9.3, point 1. f. or §11.9.6, point 7.
However, you could create some custom properties to test for, e.g.
function myBind(fn) { // takes 2+ args, the fn to bind, the new this, any other args
var bind = Function.prototype.bind,
bound = bind.call.apply(bind, arguments);
bound.unbound = fn;
return bound;
}
and then examle usage
function foo(bar) {
console.log(this, bar);
}
// binding
var fizz = myBind(foo, {buzz:0}, 'baz');
fizz(); // logs {buzz: 0} "baz"
// testing
fizz.unbound === foo; // true
If you want to test in both directions, then you will need to OR them together, and perhaps even consider looping over these properties if you will be binding already bound functions
fizz.unbound === foo || fizz === foo.unbound; // true
Please also consider that the whole chain of unbound versions of the function will not be released from memory as long as a bound version exists, whereas some browsers would have been able to free this memory, depending on their implementation of bind
Bind prepend "bound " before the source function's name.
If you can give a explicite name to the source function then you can do :
var func = function func( arg ) {
// code here
}
var myFunc = func.bind( null, 'val' );
if( myFunc.name.match(/^(bound\ )*(.*)$/i)[2] === func.name ){
console.log(true);
}
Thanks for your input #jfriend00 and #PaulS. I am using a function that automatically adds an unbound property to the bound function. Here is a refined version of what I wrote. Let me know what you think.
// My function
var myFunc = function() {
return 'This is my function';
};
// Function I'm wrapping my function in
var wrapper = function( fn ) {
var result;
if( fn ) {
result = fn.apply( null, arguments );
}
// Any additional logic I want to run after my function
console.log( 'Ran wrapper logic' );
return result;
};
// Modified binder method
var binder = function( fn, ths, args ) {
args = [].concat( ths, args );
var bound = fn.bind.apply( fn, args );
bound.unbound = fn;
return bound;
};
// Bind a wrapped version of my function
myFunc = binder( wrapper, null, myFunc );
// I can test if my function has ben wrapped
console.log( myFunc.unbound == wrapper );
// And I can run a wrapped version of my function
console.log( myFunc() );

Default value in function - Javascript

In PHP we can define the argument value for the functions if it's not set(called), so for example:
<?php
function blah($arg = false){
var_dump($arg);
}
?>
In the above example if we call the function like:
<?php
blah();
// ==> out put will be: false;
blah(true);
// ==> out put will be: true;
?>
So we can define a value for the arguments if they are not settled while we call the function, how this could be achieved in javascript functions?
I have it exactly like PHP:
<script>
function blah(arg = false){
//...
}
</script>
The above code works just fine in Mozilla Firefox, but in Chrome, the function is not working and gets fixed when I remove = false in the parenthesis, Chrome developer tools says:
Uncaught Syntax Error: Unexpected token =
This is not possible in Javascript.
Try this Conditional Operator statement instead:
<script>
function blah(arg){
arg = typeof arg !== 'undefined' ? arg : 'someValue';
}
</script>
Where 'someValue' is the default value that the arg variable will get when there are no arguments passed to the blah() function.
This is the cleanest pattern for implementing default arguments in javascript IMO.
function (arg) {
arg = arg || 'defaultVale';
}
However this can fail if you expect the value of arg to be some falsy value, e.g 0, false, NaN, null using it is not really recommended.
This version protects against this case by explicitly comparing with undefined.
function (arg) {
arg = arg === undefined ? 'defaultVale' : arg;
// As T.J Crowder pointer out if not in strict mode or if the code will run
// in an IFrame use typeof arg === "undefined" instead of directly
// comparing with undefined
}
Another nice pattern is using objects for arguments instead. This has two benefits
Order of arguments is not important
It's easy to implement default arguments
Code
var defaults = {
arg1: 10,
arg2: 20
};
var f = function (args) {
args = jQuery.extend(true, args, defaults); //jQuery
args = _.defaults(args, defaults); // Underscore
};
f({
a: 25 //Use a non default value
});
In JavaScript there is no default parameter.
Just write the code like:
function test(arg) {
if(arg) {
// do something as arg is defined.
} else {
// do something as arg is undefined.
}
}
test(true); // arg is defined
test(); // arg is undefined
Simple variation
function defaulter(p1) {
p1 = p1 || "default";
}
In Js you can't have default values for parameters. You can check if the data is of a known type with typeof operator:
function blah(arg)
{
if (typeof arg === 'undefined')
{
arg = false;
}
}
or setting his value in a short-circuit way
function blah(arg)
{
arg = arg || false;
}
For example in coffeescript you can set it by using
blah = (arg = "mydefaultvalue") ->
that is translated into
blah = function(arg)
{
if (arg == null)
{
arg = "mydefaultvalue";
}
}

Append code to the end of an existing function

I need to trigger function bar() whenever function foo() fires. I have no control over function foo or whether it will change in the future. I have this situation regularly (and I hate it).
I wrote this function to add my code to the end of function foo:
function appendToFunction(fn,code){
if(typeof fn == 'function'){
var fnCode = fn.toString() ;
fnCode = fnCode.replace(/\}$/,code+"\n}") ;
window.eval(fnCode); // Global scope
return true ;
}
else{
return false ;
}
}
eg:
appendToFunction(foo,"bar();");
This strikes me as a terrible idea - but it works. Can somebody point me in a better (safer) direction please.
EDIT: foo is not a specific function, but many functions that I wind up dealing with. They don't change dynamically in the page. But they may be changed (eg. form validation demands) from time to time.
Solution:
I settled on a modified version of Adam's Answer. It's not perfect, but it's better than what I had:
var oldFoo = foo ;
foo = function(){
var result = oldFoo.apply(this, arguments);
bar();
return result ;
}
NB. Watch out for some native functions in IE6/7 that don't have an .apply() method.
You can just override foo with a custom function that calls the original.
E.g.
var old_foo = foo;
foo = function() {
old_foo();
bar();
}
You should also pass any arguments foo takes into it via your replacement function.
function extend(fn,code){
return function(){
fn.apply(fn,arguments)
code.apply(fn,argumnets)
}
}
and use it like this:
function appendToFunction(fn,code){
if(typeof fn == 'function'){
var fnCode = fn.toString() ;
fnCode = fnCode.replace(/\}$/,code+"\n}") ;
window.eval(fnCode); // Global scope
return true ;
}
else{
return false ;
}
}
appendToFunction = extend(appendToFunction,function(){/*some code*/});
this will give you the same "this" in both functions
You could do something like this: THE DEMO.
function foo() {
console.log('foo');
}
function appendToFunction(fn, callback) {
window[fn] = (function(fn){
return function() {
fn();
callback();
}
}(window[fn]));
}
appendToFunction('foo', function(){console.log('appended!')});
foo();
Hmm, this concerns me as well, you mentioned that
I have this situation regularly (and I hate it).
Do you mind if I ask in what scenario this keeps occurring? Is it in a corporate scale, or on a personal project scope? You've clearly got a level head on your shoulders and know that what you're doing is out of the ordinary, so I'm wondering if there is an alternatively solution.
The reason I ask is; this approach could potentially open a can of problems. What if foo fails for example, or if foo returns a value mid evaluation? By simply appending bar to the actual function doesn't guarantee it will execute. Pre-pending it on the other hand means it's more likely to be executed, but still in my opinion isn't a good approach.
Have you considered revising the function foo? I know this might seem like a silly question, but it might be worth it if you're encountering similar problems throughout. If you want to keep things abstracted you could adopt an "event handler" approach, whereby foo triggers an event on the window, which in turn then triggers bar, would this work in your case.
Alternatively, if you know what foo is, and what it does, you could hook into it's prototype if it's an object, and then amend the code there appropriately. You did however mention that this function is open to change, which may make this option redundant, but it's a possible solution nonetheless.
You can append or prepend some new code to an existing function just merging them using for example:
function mergeFunctions(function1, function2, instance1, instance2, numberOfArgumentsToPassToFunc1) {
return function() {
var _arguments = Array.prototype.slice.apply(arguments);
var _arguments1 = _arguments.slice(0, numberOfArgumentsToPassToFunc1);
var _arguments2 = _arguments.slice(numberOfArgumentsToPassToFunc1);
var that = this;
(function(function1, function2) {
if (typeof function1 == "function") {
if (typeof instance1 != "undefined") {
function1.apply(instance1, _arguments1);
}
else if (that == window) {
function1.apply(function1, _arguments1);
}
else {
var compare = mergeFunctions(function(){}, function(){});
if (that.toString() == compare.toString()) {
function1.apply(function1, _arguments1);
}
else {
function1.apply(that, _arguments1);
}
}
}
if (typeof function2 == "function") {
if (typeof instance2 != "undefined") {
function2.apply(instance2, _arguments2);
}
else if (that == window) {
function2.apply(function2, _arguments2);
}
else {
var compare = mergeFunctions(function(){}, function(){});
if (that.toString() == compare.toString()) {
function2.apply(function2, _arguments2);
}
else {
function2.apply(that, _arguments2);
}
}
}
})(function1, function2);
}
}
A basic example would be the following:
// Original function:
var someFunction = function(){
console.log("original content");
};
// Prepend new code:
// --------------------------------------------------------
someFunction = mergeFunctions(function() {
console.log("--- prepended code");
}, someFunction);
// Testing:
someFunction();
// Outout:
// [Log] --- prepended code
// [Log] original content
// Append new code:
// --------------------------------------------------------
someFunction = mergeFunctions(someFunction, function() {
console.log("appended code");
});
// Testing:
someFunction();
// Output:
// [Log] --- prepended code
// [Log] original content
// [Log] appended code
Note that the merging function tries to apply the expected 'this' to the merged parts, otherwise you can just simply pass the wanted 'this' to them, as well as you can handle the relative arguments.
A more general example could be the following:
function firstPart(a, b) {
console.log("--- first part");
console.log("'this' here is:");
console.log(this.name);
console.log("a: "+a);
console.log("b: "+b);
}
function MyObject() {
this.x = "x property of MyObject";
}
MyObject.prototype.secondPart = function (y) {
console.log("");
console.log("--- second part");
console.log("'this' here is:");
console.log(this.name);
this.x = y;
console.log("x: "+this.x);
}
MyObject.prototype.merged = mergeFunctions(firstPart, MyObject.prototype.secondPart, firstPart, MyObject, 2);
// Testing
var test = new MyObject();
test.merged("a parameter", "b parameter", "x parameter overrides x property of MyObject");
// Console output:
// [Log] --- first part
// [Log] 'this' here is:
// [Log] firstPart
// [Log] a: a parameter
// [Log] b: b parameter
// [Log]
// [Log] --- second part
// [Log] 'this' here is:
// [Log] MyObject
// [Log] x: x parameter overrides x property of MyObject

Guarding against undefined parameters

So I'm using an object to pass in my optional variables like so:
var foo = function (options) {
var options = options || {}; //provide a default, so that
var bar = options.bar || 42; //this doesn't fail on 'foo();'
//do stuff
};
JSLint complains that I'm overwriting options, which is what I want if it's falsy. Actually, I should probably check if it's an Object and elsewise throw an error to let the user know what's up. So - on that note - what would be good form here? Anybody know a good read on this? Additionally, how would I go about writing a bunch of functions with this pattern in a DRY style?
First, you need to not reassign the options argument to a var. Second, be careful with this for certain values:
var foo = function (options) {
options = options || {};
var bar = options.bar || 42;
var baz = options.baz || 'fubar';
};
foo({ bar: 0, baz: "" });
Inside foo, bar == 42 & baz == 'fubar' because 0 and "" are falsy. It's probably better to be more verbose so as to be more precise:
var foo = function (options) {
options = options || {};
var bar = typeof options.bar == 'undefined' ? 42 : options.bar;
var baz = typeof options.baz == 'undefined' ? 'fubar' : options.baz;
};
But, to be DRY, you can create a defaults object and just extend both objects to a settings object:
// generic shallow object extension, mutates obj1
var extend = function (obj1, obj2) {
for (var prop in obj2) {
if (obj2.hasOwnProperty(prop)) {
obj1[prop] = obj2[prop];
}
}
return obj1;
};
var foo = function (options) {
var defaults = {
bar: 42,
baz: 'fubar'
},
settings = extend(defaults, options);
};
Since you won't be using defaults any more in this invocation it doesn't matter that it gets mutated. Using this method:
foo(); // -> settings = { bar: 42, baz: 'fubar' }
foo({ bar: 0 }); // -> settings = { bar: 0, baz: 'fubar' }
foo({ bar: 0, baz: "" }); // -> settings = { bar: 0, baz: '' }
Remove the var from the options line and JSLint will stop complaining.
It's because you have a parameter called options, and then you are trying to declare a local variable called options. You can still set the default value if it's falsey
options = options || {};
To be honest, the null coalescing in JavaScript is pretty fine on it's own.
You can remember that you can be a bit 'exotic' with it, if you really want. So, for your example, if you just wanted options.bar or 42:
(options || {})["bar"] || 42
(remembering that you can access JavaScript properties by dot notation or array style)
It's not pretty - but it's just one example. Alternatively, you could do something like:
(options || {"bar":42})["bar"]
If you had a default set of options, you can do:
(options || defaults)["bar"]
Note : As mentioned in a different answer, you should be careful before certain values are falsely that you might not realise (i.e. an empty string). Of course, sometimes this is exactly what you want but it's just something to remember :)
JSLint is only complaining because you are using the var keyword, but options has already been defined. You can just remove the var and it won't complain.
To test if options is an object you can use typeof options === 'object', but note that arrays and null are also objects, so you may want to test something like:
typeof options === 'object' && options !== null && !(options instanceof Array)

Categories

Resources