Make Javascript local variable to global for recursive loops - javascript

I have a recursive function which has a local variable.
It calls itself on specific condition.
The local variable needs to be updated, but every call it creates a new local variable specific to the current function scope.
How can i reach the local variable for access all recursive loop and not to create a new one?
Something like __Callee.varname?
The code is:
var addAttribute = function(object,elem)
{
var attributes = [];
// only attribute without values
if ( object instanceof Array )
{
for ( var value in object )
{
attributes.push(object[value]);
}
}
// attribute with values
else if ( object instanceof Object )
{
for ( var key in object )
{
if ( object[key] instanceof Array )
{
addAttribute(object[key],elem);
}
else
{
attributes.push(key+'=\''+object[key]+'\'');
}
}
}
// Only one attribute
else if ( typeof object === 'string' )
{
attributes.push('\''+object+'\'');
}
// Invalid parameter
else
{
console.log('Invalid parameter: '+typeof object);
}
console.log('<'+elem+' '+attributes.join(' ').toString()+' />');
}
I do not want to make variable to global because of using this name in other functions and global scope already.

Use a closure
function fn() {
function recursiveFunction() {
// do something with x
recursiveFunction();
}
var x = 0;
recursiveFunction();
}

The usual thing is to pass it into the function, possibly optionally:
var addAttribute = function(object,elem, attributes) {
attributes = attributes || [];
// ....
Then when calling it recursively, pass in the third argument:
addAttribute(object[key], value, attributes);
Here's a much simplified example demonstrating:
function foo(num, array) {
array = array || [];
array.push(num);
console.log("Pushed " + num + ", array = " + JSON.stringify(array));
if (num < 5) {
foo(num + 1, array);
}
}
foo(1);

Related

Recursive function, maintain a global counter without using a global variable

I'm writing functions to serialize and deserialize a binary tree (just converting it to an array and back to a BT) and I'm having a hard time writing the deserializing function without using a global variable to keep track of the index.
my Node class looks like this:
class Node {
constructor(value) {
this.value = value
this.left = null
this.right = null
}
}
and my serialize method looks like this:
function serialize(node = this, list = []) {
if (!node) {
list.push('#')
return
}
list.push(node.value)
serialize(node.left, list)
serialize(node.right, list)
return list
}
Here's my problem, in the deserialize function I have to keep a global variable for the 'index', is there a way to keep the global value for the index without creating a global variable?
let index = 0
function deserialize(list) {
if (index === list.length || list[index] === '#') {
index++
return
}
const tree = new Node(list[index])
index++
tree.left = deserialize(list)
tree.right = deserialize(list)
return tree
}
I initially wrote the function like this: (but I had to refactor the index variable to a global variable)
function deserialize(list, index = 0) {
if (index === list.length || list[index] === '#') {
index++
return
}
const tree = new Node(list[index])
index++
tree.left = deserialize(list, index)
tree.right = deserialize(list, index)
return tree
}
This ended up with a symetric tree because the tree.left and tree.right would always take the same index.
I'm just curious if there is a simple way to keep track of the index in a purely recursive function (not using a recursive subroutine) without creating a global variable.
A general way to do that:
function wrapperFunction(args) {
var bookkeepingStuff = whatever;
function actualRecursiveFunction(args) {
// code
}
return actualRecursiveFunction(args);
}
Just wrap the real recursive function in another function. Any local variables of the wrapper will be effectively like global variables to the real recursive function nested inside.
Pass a baton with a member you can update as you recurse:
function deserialize(list, baton={index: 0}) {
var b = baton; // shorthand
if (b.index === list.length || list[b.index] === '#') {
b.index++;
return;
}
const tree = new Node(list[b.index]);
b.index++;
tree.left = deserialize(list, b);
tree.right = deserialize(list, b);
return tree;
}

Object has-property-deep check in JavaScript

Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});

calling object properties in N levels javascript

I'm using mongodb and ajax calls to retrieve data. When it turns to javascript object, the properties that I use to generate html sometimes don't exist.
Look at this call:
$.ajax({
url: 'api/v1/mention/'+id,
type: "GET",
dataType: "json",
data : {login : "demo"},
success: function(mention) {
display_mention_text(mention.texto);
}
});
In this case i'm calling mention.texto, but could be mention.picture or any properties. Sometimes it is undefined and crashes the app.
This method calls a property from a object and if its undefined , return an empty string.
Some examples for calling this method(the first one is an object, the other are properties):
get_property(mention,"text")
get_property(mention,"user","name")
get_property(mention,"picture")
The method is defined as follows:
function get_property(obj){
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
if (checkNested(obj,args)) {
//what should I do here?
} else{
//the property is undefined and returns ""
"";
};
}
//check if a object has N levels of propertys
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
In the first method get_property, if the property do exist, how do I call that??
I would have the object and his propertys as an array like:
object
params = ["user","name"]
but I can't call like following:
object.["user","name"]
Replace the if statement in the get_property function with the for loop from the checkNested function. Then instead of returning true or false, return the value found or "".
function get_property(obj){
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
// Here's your 'for' loop. The 'if' statement is gone.
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return ""; // I changed the value of this 'return' statement
}
obj = obj[args[i]];
}
return obj; // I change the value of this 'return' statement
}
Again, all I did was copy paste your own code from one function to the other, and change the values of the return statements.
You can achieve your goal by this way (no need of checkNested function):
//Parameters {obj, prop1, prop2, ... propN}
function get_property(){
var args = Array.prototype.slice.call(arguments),
obj = args.shift(),
prop = args.shift();
if( obj.hasOwnProperty(prop) ){
if( args.length > 0 ){
//Calling 'get_property' with {obj[prop1], prop2, ... propN} , and so on
return get_property.apply(get_property, [obj[prop]].concat(args));
}else{
return obj[prop];
}
}else{
return "";
}
}​
Usage:
var o = {
"a" : {
"b" : 5
}
};
console.log( get_property(o,"c") ); // ""
console.log( get_property(o,"a","b") ); // 5
console.log( get_property(o,"a") ); // {"b":5}
You're just looking for a simple recursive function.
function get_property(obj, prop) {
if (obj[prop]) {
return obj[prop];
}
else {
var end;
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
end = get_property(obj[p], prop);
}
}
if (!end) {
// If we're there, it means there's no property in any nested level
return;
}
else {
return end;
}
}
}
Edit: looks like I misinterpreted your question. See #Engineer's answer instead. Keeping this answer in case someone looks like for something like this.
You're overcomplicating things. If you know the tree of your object, just call:
obj['user']['name'];
If you're expecting undefined properties (and really, that means there's something else wrong):
var prop;
try {
prop = obj.user.name;
}
catch (e) {
prop = null;
}

How to overload functions in javascript?

Classical (non-js) approach to overloading:
function myFunc(){
//code
}
function myFunc(overloaded){
//other code
}
Javascript wont let more than one function be defined with the same name. As such, things like this show up:
function myFunc(options){
if(options["overloaded"]){
//code
}
}
Is there a better workaround for function overloading in javascript other than passing an object with the overloads in it?
Passing in overloads can quickly cause a function to become too verbose because each possible overload would then need a conditional statement. Using functions to accomplish the //code inside of those conditional statements can cause tricky situations with scopes.
There are multiple aspects to argument overloading in Javascript:
Variable arguments - You can pass different sets of arguments (in both type and quantity) and the function will behave in a way that matches the arguments passed to it.
Default arguments - You can define a default value for an argument if it is not passed.
Named arguments - Argument order becomes irrelevant and you just name which arguments you want to pass to the function.
Below is a section on each of these categories of argument handling.
Variable Arguments
Because javascript has no type checking on arguments or required qty of arguments, you can just have one implementation of myFunc() that can adapt to what arguments were passed to it by checking the type, presence or quantity of arguments.
jQuery does this all the time. You can make some of the arguments optional or you can branch in your function depending upon what arguments are passed to it.
In implementing these types of overloads, you have several different techniques you can use:
You can check for the presence of any given argument by checking to see if the declared argument name value is undefined.
You can check the total quantity or arguments with arguments.length.
You can check the type of any given argument.
For variable numbers of arguments, you can use the arguments pseudo-array to access any given argument with arguments[i].
Here are some examples:
Let's look at jQuery's obj.data() method. It supports four different forms of usage:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Each one triggers a different behavior and, without using this dynamic form of overloading, would require four separate functions.
Here's how one can discern between all these options in English and then I'll combine them all in code:
// get the data element associated with a particular key value
obj.data("key");
If the first argument passed to .data() is a string and the second argument is undefined, then the caller must be using this form.
// set the value associated with a particular key
obj.data("key", value);
If the second argument is not undefined, then set the value of a particular key.
// get all keys/values
obj.data();
If no arguments are passed, then return all keys/values in a returned object.
// set all keys/values from the passed in object
obj.data(object);
If the type of the first argument is a plain object, then set all keys/values from that object.
Here's how you could combine all of those in one set of javascript logic:
// method declaration for .data()
data: function(key, value) {
if (arguments.length === 0) {
// .data()
// no args passed, return all keys/values in an object
} else if (typeof key === "string") {
// first arg is a string, look at type of second arg
if (typeof value !== "undefined") {
// .data("key", value)
// set the value for a particular key
} else {
// .data("key")
// retrieve a value for a key
}
} else if (typeof key === "object") {
// .data(object)
// set all key/value pairs from this object
} else {
// unsupported arguments passed
}
},
The key to this technique is to make sure that all forms of arguments you want to accept are uniquely identifiable and there is never any confusion about which form the caller is using. This generally requires ordering the arguments appropriately and making sure that there is enough uniqueness in the type and position of the arguments that you can always tell which form is being used.
For example, if you have a function that takes three string arguments:
obj.query("firstArg", "secondArg", "thirdArg");
You can easily make the third argument optional and you can easily detect that condition, but you cannot make only the second argument optional because you can't tell which of these the caller means to be passing because there is no way to identify if the second argument is meant to be the second argument or the second argument was omitted so what's in the second argument's spot is actually the third argument:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Since all three arguments are the same type, you can't tell the difference between different arguments so you don't know what the caller intended. With this calling style, only the third argument can be optional. If you wanted to omit the second argument, it would have to be passed as null (or some other detectable value) instead and your code would detect that:
obj.query("firstArg", null, "thirdArg");
Here's a jQuery example of optional arguments. both arguments are optional and take on default values if not passed:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Here's a jQuery example where the argument can be missing or any one of three different types which gives you four different overloads:
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
Named Arguments
Other languages (like Python) allow one to pass named arguments as a means of passing only some arguments and making the arguments independent of the order they are passed in. Javascript does not directly support the feature of named arguments. A design pattern that is commonly used in its place is to pass a map of properties/values. This can be done by passing an object with properties and values or in ES6 and above, you could actually pass a Map object itself.
Here's a simple ES5 example:
jQuery's $.ajax() accepts a form of usage where you just pass it a single parameter which is a regular Javascript object with properties and values. Which properties you pass it determine which arguments/options are being passed to the ajax call. Some may be required, many are optional. Since they are properties on an object, there is no specific order. In fact, there are more than 30 different properties that can be passed on that object, only one (the url) is required.
Here's an example:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
Inside of the $.ajax() implementation, it can then just interrogate which properties were passed on the incoming object and use those as named arguments. This can be done either with for (prop in obj) or by getting all the properties into an array with Object.keys(obj) and then iterating that array.
This technique is used very commonly in Javascript when there are large numbers of arguments and/or many arguments are optional. Note: this puts an onus on the implementating function to make sure that a minimal valid set of arguments is present and to give the caller some debug feedback what is missing if insufficient arguments are passed (probably by throwing an exception with a helpful error message).
In an ES6 environment, it is possible to use destructuring to create default properties/values for the above passed object. This is discussed in more detail in this reference article.
Here's one example from that article:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Then, you can call this like any of these:
selectEntries({start: 5});
selectEntries({start: 5, end: 10});
selectEntries({start: 5, end: 10, step: 2});
selectEntries({step: 3});
selectEntries();
The arguments you do not list in the function call will pick up their default values from the function declaration.
This creates default properties and values for the start, end and step properties on an object passed to the selectEntries() function.
Default values for function arguments
In ES6, Javascript adds built-in language support for default values for arguments.
For example:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Further description of the ways this can be used here on MDN.
Overloading a function in JavaScript can be done in many ways. All of them involve a single master function that either performs all the processes, or delegates to sub-functions/processes.
One of the most common simple techniques involves a simple switch:
function foo(a, b) {
switch (arguments.length) {
case 0:
//do basic code
break;
case 1:
//do code with `a`
break;
case 2:
default:
//do code with `a` & `b`
break;
}
}
A more elegant technique would be to use an array (or object if you're not making overloads for every argument count):
fooArr = [
function () {
},
function (a) {
},
function (a,b) {
}
];
function foo(a, b) {
return fooArr[arguments.length](a, b);
}
That previous example isn't very elegant, anyone could modify fooArr, and it would fail if someone passes in more than 2 arguments to foo, so a better form would be to use a module pattern and a few checks:
var foo = (function () {
var fns;
fns = [
function () {
},
function (a) {
},
function (a, b) {
}
];
function foo(a, b) {
var fnIndex;
fnIndex = arguments.length;
if (fnIndex > foo.length) {
fnIndex = foo.length;
}
return fns[fnIndex].call(this, a, b);
}
return foo;
}());
Of course your overloads might want to use a dynamic number of parameters, so you could use an object for the fns collection.
var foo = (function () {
var fns;
fns = {};
fns[0] = function () {
};
fns[1] = function (a) {
};
fns[2] = function (a, b) {
};
fns.params = function (a, b /*, params */) {
};
function foo(a, b) {
var fnIndex;
fnIndex = arguments.length;
if (fnIndex > foo.length) {
fnIndex = 'params';
}
return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
}
return foo;
}());
My personal preference tends to be the switch, although it does bulk up the master function. A common example of where I'd use this technique would be a accessor/mutator method:
function Foo() {} //constructor
Foo.prototype = {
bar: function (val) {
switch (arguments.length) {
case 0:
return this._bar;
case 1:
this._bar = val;
return this;
}
}
}
You cannot do method overloading in strict sense. Not like the way it is supported in java or c#.
The issue is that JavaScript does NOT natively support method overloading. So, if it sees/parses two or more functions with a same names it’ll just consider the last defined function and overwrite the previous ones.
One of the way I think is suitable for most of the case is follows -
Lets say you have method
function foo(x)
{
}
Instead of overloading method which is not possible in javascript you can define a new method
fooNew(x,y,z)
{
}
and then modify the 1st function as follows -
function foo(x)
{
if(arguments.length==2)
{
return fooNew(arguments[0], arguments[1]);
}
}
If you have many such overloaded method consider using switch than just if-else statements.
(more details)
PS: Above link goes to my personal blog that has additional details on this.
I am using a bit different overloading approach based on arguments number.
However i believe John Fawcett's approach is also good.
Here the example, code based on John Resig's (jQuery's Author) explanations.
// o = existing object, n = function name, f = function.
function overload(o, n, f){
var old = o[n];
o[n] = function(){
if(f.length == arguments.length){
return f.apply(this, arguments);
}
else if(typeof o == 'function'){
return old.apply(this, arguments);
}
};
}
usability:
var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)
I tried to develop an elegant solution to this problem described here. And you can find the demo here. The usage looks like this:
var out = def({
'int': function(a) {
alert('Here is int '+a);
},
'float': function(a) {
alert('Here is float '+a);
},
'string': function(a) {
alert('Here is string '+a);
},
'int,string': function(a, b) {
alert('Here is an int '+a+' and a string '+b);
},
'default': function(obj) {
alert('Here is some other value '+ obj);
}
});
out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);
The methods used to achieve this:
var def = function(functions, parent) {
return function() {
var types = [];
var args = [];
eachArg(arguments, function(i, elem) {
args.push(elem);
types.push(whatis(elem));
});
if(functions.hasOwnProperty(types.join())) {
return functions[types.join()].apply(parent, args);
} else {
if (typeof functions === 'function')
return functions.apply(parent, args);
if (functions.hasOwnProperty('default'))
return functions['default'].apply(parent, args);
}
};
};
var eachArg = function(args, fn) {
var i = 0;
while (args.hasOwnProperty(i)) {
if(fn !== undefined)
fn(i, args[i]);
i++;
}
return i-1;
};
var whatis = function(val) {
if(val === undefined)
return 'undefined';
if(val === null)
return 'null';
var type = typeof val;
if(type === 'object') {
if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
return 'array';
if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
return 'date';
if(val.hasOwnProperty('toExponential'))
type = 'number';
if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
return 'string';
}
if(type === 'number') {
if(val.toString().indexOf('.') > 0)
return 'float';
else
return 'int';
}
return type;
};
In javascript you can implement the function just once and invoke the function without the parameters myFunc() You then check to see if options is 'undefined'
function myFunc(options){
if(typeof options != 'undefined'){
//code
}
}
https://github.com/jrf0110/leFunc
var getItems = leFunc({
"string": function(id){
// Do something
},
"string,object": function(id, options){
// Do something else
},
"string,object,function": function(id, options, callback){
// Do something different
callback();
},
"object,string,function": function(options, message, callback){
// Do something ca-raaaaazzzy
callback();
}
});
getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"
No Problem with Overloading in JS , The pb how to maintain a clean code when overloading function ?
You can use a forward to have clean code, based on two things:
Number of arguments (when calling the function).
Type of arguments (when calling the function)
function myFunc(){
return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
}
/** one argument & this argument is string */
function myFunc_1_string(){
}
//------------
/** one argument & this argument is object */
function myFunc_1_object(){
}
//----------
/** two arguments & those arguments are both string */
function myFunc_2_string_string(){
}
//--------
/** Three arguments & those arguments are : id(number),name(string), callback(function) */
function myFunc_3_number_string_function(){
let args=arguments;
new Person(args[0],args[1]).onReady(args[3]);
}
//--- And so on ....
How about using a proxy (ES6 Feature)?
I didn't find anywhere mentioning this method of doing it. It might be impractical but it's an interesting way nonetheless.
It's similar to Lua's metatables, where you can "overload" the call operator with the __call metamethod in order to achieve overloading.
In JS, it can be done with the apply method in a Proxy handler. You can check the arguments' existence, types, etc. inside the said method, without having to do it in the actual function.
MDN: proxy apply method
function overloads() {}
overloads.overload1 = (a, b) => {
return a + b;
}
overloads.overload2 = (a, b, c) => {
return a + b + c;
}
const overloadedFn = new Proxy(overloads, { // the first arg needs to be an Call-able object
apply(target, thisArg, args) {
if (args[2]) {
return target.overload2(...args);
}
return target.overload1(...args);
}
})
console.log(overloadedFn(1, 2, 3)); // 6
console.log(overloadedFn(1, 2)); // 3
Check this out:
http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions
Basically in your class, you number your functions that you want to be overloaded and then with one function call you add function overloading, fast and easy.
Since JavaScript doesn't have function overload options object can be used instead. If there are one or two required arguments, it's better to keep them separate from the options object. Here is an example on how to use options object and populated values to default value in case if value was not passed in options object.
function optionsObjectTest(x, y, opts) {
opts = opts || {}; // default to an empty options object
var stringValue = opts.stringValue || "string default value";
var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;
return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";
}
here is an example on how to use options object
For this you need to create a function that adds the function to an object, then it will execute depending on the amount of arguments you send to the function:
<script >
//Main function to add the methods
function addMethod(object, name, fn) {
var old = object[name];
object[name] = function(){
if (fn.length == arguments.length)
return fn.apply(this, arguments)
else if (typeof old == 'function')
return old.apply(this, arguments);
};
}
 var ninjas = {
values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};
//Here we declare the first function with no arguments passed
addMethod(ninjas, "find", function(){
return this.values;
});
//Second function with one argument
addMethod(ninjas, "find", function(name){
var ret = [];
for (var i = 0; i < this.values.length; i++)
if (this.values[i].indexOf(name) == 0)
ret.push(this.values[i]);
return ret;
});
//Third function with two arguments
addMethod(ninjas, "find", function(first, last){
var ret = [];
for (var i = 0; i < this.values.length; i++)
if (this.values[i] == (first + " " + last))
ret.push(this.values[i]);
return ret;
});
//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>
How about using spread operator as a parameter? The same block can be called with Multiple parameters. All the parameters are added into an array and inside the method you can loop in based on the length.
function mName(...opt){
console.log(opt);
}
mName(1,2,3,4); //[1,2,3,4]
mName(1,2,3); //[1,2,3]
I like to add sub functions within a parent function to achieve the ability to differentiate between argument groups for the same functionality.
var doSomething = function() {
var foo;
var bar;
};
doSomething.withArgSet1 = function(arg0, arg1) {
var obj = new doSomething();
// do something the first way
return obj;
};
doSomething.withArgSet2 = function(arg2, arg3) {
var obj = new doSomething();
// do something the second way
return obj;
};
What you are trying to achieve is best done using the function's local arguments variable.
function foo() {
if (arguments.length === 0) {
//do something
}
if (arguments.length === 1) {
//do something else
}
}
foo(); //do something
foo('one'); //do something else
You can find a better explanation of how this works here.
(() => {
//array that store functions
var Funcs = []
/**
* #param {function} f overload function
* #param {string} fname overload function name
* #param {parameters} vtypes function parameters type descriptor (number,string,object....etc
*/
overloadFunction = function(f, fname, ...vtypes) {
var k,l, n = false;
if (!Funcs.hasOwnProperty(fname)) Funcs[fname] = [];
Funcs[fname].push([f, vtypes?vtypes: 0 ]);
window[fname] = function() {
for (k = 0; k < Funcs[fname].length; k++)
if (arguments.length == Funcs[fname][k][0].length) {
n=true;
if (Funcs[fname][k][1]!=0)
for(i=0;i<arguments.length;i++)
{
if(typeof arguments[i]!=Funcs[fname][k][1][i])
{
n=false;
}
}
if(n) return Funcs[fname][k][0].apply(this, arguments);
}
}
}
})();
//First sum function definition with parameter type descriptors
overloadFunction(function(a,b){return a+b},"sum","number","number")
//Second sum function definition with parameter with parameter type descriptors
overloadFunction(function(a,b){return a+" "+b},"sum","string","string")
//Third sum function definition (not need parameter type descriptors,because no other functions with the same number of parameters
overloadFunction(function(a,b,c){return a+b+c},"sum")
//call first function
console.log(sum(4,2));//return 6
//call second function
console.log(sum("4","2"));//return "4 2"
//call third function
console.log(sum(3,2,5));//return 10
//ETC...

Setting variable by string name in javascript?

//window["Fluent"]["Include"]
function setGlobalVariableByName(name,value)
{
var indexes = name.split(".");
var variable = null;
$.each(indexes, function()
{
if (variable == null){
variable = window[this];
}else{
variable = variable[this];
}
});
variable = value;
}
setGlobalVariableByName("Fluent.Include.JqueryPulse",true);
console.log(Fluent.Include.JqueryPulse) // prints false
this doesn't work, obviously. It would work if I just wanted to get the variable's value, but not for setting it.
window["Fluent"]["Include"]["JqueryPulse"] = true;
console.log(Fluent.Include.JqueryPulse) // prints true
how could I achieve something like this without using eval?
I'd need some way to programmatically add array indices to this, I'd guess
The following works, can you suggest a better way to code it in order to make it more DRY?
function setGlobalVariableByName(name,value)
{
var indices = name.split(".");
var parent;
$.each(indices, function(i)
{
if(i==indices.length-1){
if (!parent){
window[this] = value;
}else{
parent[this] = value;
}
}else if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
}
setGlobalVariableByName : function(name, value)
{
var indices = name.split(".");
var last = indices.pop();
var parent;
$.each(indices, function(i)
{
if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
if (!parent){
window[last] = value;
}else{
parent[last] = value;
}
}
You need to call
variable[this] = value
somehow. So you need to break the loop of the splited string before reching the last name, and then assign the value.
Ultimatively you need to call:
variable = window['Fluent']['Include']; // build this in a loop
variable['JqueryPulse'] = someValue; // then call this
Ultimately you're just building an object chain and setting the final item in the chain to a value. Also, I would add a check to ensure that items which are already objects do not get overwritten so that their existing properties don't get lost:
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
$.each( names, function()
{
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[this] !== 'object' || currentOp[this] === null )
{
currentOp[this] = {};
}
//move the reference for the next iteration
currentOp = currentOp[this];
} );
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );
However, I would do it without using jQuery, even if you have jQuery available on the page. There is no need for the overhead of executing a function for each index.
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
indexCount,
currentIndex,
currentName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
indexCount = names.length;
for( currentIndex = 0; currentIndex < indexCount; currentIndex += 1 )
{
currentName = names[currentIndex];
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[currentName] !== 'object' || currentOp[currentName] === null )
{
currentOp[currentName] = {};
}
//move the reference for the next iteration
currentOp = currentOp[currentName];
}
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );

Categories

Resources