I know it has been asked and answered how to set a default parameter for a javascript function by Tilendor
I was wondering if I had a function containing a series of booleans.
For example, a function called 'anAnimal' and as a default I set a value of false to three propertys. fly, swim, and climb.
What would I do if I just want to pass in an argument of true to my property called climb?
Currently when calling in the method I have to pass in a value of false for fly and swim even though I have already predefined them as false.
This is what I am attempting and I would like the result to say true instead of false
function anAnimal (fly = false, swim = false, climb = false) {
this.canFly = fly;
this.canSwim = swim;
this.canClimb = climb;
}
var cat = new anAnimal(true)
var theCat = 'This cat can climb: ' + cat.canClimb;
document.write(theCat);
You can pass an object instead of arguments and populate default option based on inputs.
function anAnimal(obj) {
var defaults = {
fly: false,
swim: false,
climb: false
}
//copy the values of all enumerable own properties from one or more source objects to a target object.
Object.assign(defaults, obj);
//Assign the values
this.canFly = defaults.fly;
this.canSwim = defaults.swim;
this.canClimb = defaults.climb;
}
var cat = new anAnimal({
climb: true
});
var theCat = 'This cat can climb: ' + cat.canClimb;
document.write(theCat);
However I would recommend to use setters i.e. setClimb()
In You don't know JS type & grammar, a design pattern called "dependency injection" is shown in the end of the chapter 1, which I know is not point of this chapter, but I was confused by the example.
the example code is here:
function doSomethingCool(FeatureXYZ) {
var helper = FeatureXYZ ||
function() { /*.. default feature ..*/ };
var val = helper();
// ..
}
So I want to use this function.
Because I don't know whether the FeatureXYZ exists, I dont know how to use it.
doSomethingCool() will not use the FeatureXYZ and doSomethingCool(FeatureXYZ) will throw error if no FeatureXYZ exists. Thus, the function may be a meaningless function.
You can use default parameters to set a default function to call if no parameter is passed, or use the parameter instead if passed to the function.
Use bracket notation to reference the property "FeatureXYZ" at either window object or the object which "FeatureXYZ" is expected to be a property. Bracket notation should not throw an error if the property is not defined at the object.
function FeatureXYZ () {
return {def:456}
}
function doSomethingCool(FeatureXYZ = window["FeatureXYZ"] /* obj["FeatureXYZ"] */) {
var helper = FeatureXYZ || function config() {
/*.. default feature ..*/
return {abc:123}
};
var val = helper();
// ..
return val
}
console.log(doSomethingCool());
Basically, the || operator returns the first value if it's not null or undefined. If it is, it returns the second value. Example:
var x = null;
var y = x || 5;
x is null, hence z will be set equal to 5. If x would be, for example, 4, y would be set to 4 as well.
JavaScript has the feature (or oddity, depends on the viewer), that you do not have to pass every parameter a function has. For example, if you have the following function:
function(x, y) {
return x * (y || 5);
}
It would not result in an error if you call the function without passing y, because it would multiply x by 5 if y is not provided (and therefore undefined).
This is how the example works: It sets helper equal to FeatureXYZ, or, if it is not passed as an argument, e.g. doSomethingCool() (and therefore undefined), it is set to a default function. That way later in the code, when you execute helper(), you either use the passed function, or the default one, if it's not given.
I am trying to use some code from this tutorial and it contains some strange javascript notation that I am not familiar with chart.attr = function(name, value) {... . More than it being unfamiliar to me, it is throwing errors. I am trying to figure out how it can be changes to work in pure javascript.
function LineChart(config) {
function chart() {
// Draw the line.
chartContainer.append("path")
.datum(p.data)
.attr("class", "line")
.attr("d", line);
}
// **** This is the notation I do not understand, and gives me errors ****
chart.attr = function(name, value) {
if (arguments.length == 1)
{
return p[name];
}
else if (arguments.length == 2)
{
p[name] = value;
}
return chart;
}
chart.update = function() {
}
return chart;
}
Your code is trying to use a variable p which is undefined. It should be defined in the LineChart function as:
function LineChart(config) {
var p =
{
parent : null,
labels : [ "X", "Y" ],
...
};
...
}
As for the notation that you don't understand, this is an anonymous function expression which is being assigned to the chart.attr property. Even though it can be called by chart.attr(), this is still an anonymous function because it doesn't have a name.
The purpose of this particular function is to be a getter and setter for properties of the p object. It looks at the arguments to determine the way the function should behave: if there is only one argument, then it needs to return the property value, if there are two arguments then it should set the property value.
Example usage would look like:
var c = new LineChart();
var parent = c.attr('parent'); // get the value of the parent property
c.attr('parent', $('#something')); // set the value of the parent property
Let's dissect that line of code:
//Define chart.attr as a function that by default takes 2 parameters;
chart.attr = function(name, value) {
//If the function only gets 1 argument (so the first one)
if (arguments.length == 1)
{
//return the element with key "name" from the array p
//effectively a getter
return p[name];
}
// else, check if there are 2 arguments, but no more
else if (arguments.length == 2)
{
Assign the value of "value" to the element with key "name" from p
effectively a setter;
p[name] = value;
}
//at the end, return the chart
return chart;
}
So what this piece of code does is that if you pass only 1 argument to chart.attr(), it retrieves the value associated with that key from the array p. If you pass 2 arguments, it uses the second argument as the value of the key-valuepair from the array p with the first argument as the key.
now, without knowing the error you get, it's hard to debug this. However, the only way in which this would give an error is if p is undefined. if p doesn't contain that key, it returns null if it's a getter, and creates if it's a setter.
There is another way for this code to fail. And since the op didn't provide the error I will just speculate.
This can fail if you call, for example, chart.attr('somekey','somevalue') before chart.attr = function(name,value) { } is executed. This happens because of function hoisting...you are assigning a value to a property in this line of code. You're not defining a function...you're assigning one.
If you call chart.attr('somekey','somevalue') in the above conditions, you'll get a chart.attr is not a function error.
How do you achieve the following thing in Javascript
1) var MyObject={
2) tableView:true,
3) chartView:!(this.tableView)
4) }
The code at line number 3 is not working. Whats wrong in that line ?
What i basically want to do is set "chartView" to opposite of "tableView" whenever tableView is set from code.
Since you're in the process of creating the object, this is not bound to that object. Moreover, since you want chartView to always evaluate to the opposite of tableView, even if the latter changes further down the line, a function would be a better approach:
var MyObject = {
tableView: true,
chartView: function() {
return !this.tableView;
}
};
Now you can do:
var chartView = MyObject.chartView(); // false.
MyObject.tableView = false;
chartView = MyObject.chartView(); // true.
You can't use this to refer to an object in an object literal's properties. You can use this inside a function that is a method of that object:
var MyObject = {
tableView: true,
chartView: function () {
return !this.tableView;
}
}
Based on your requirement, this may be an answer too,
var MyObject = {
view : function(bool){
this.tableView = bool;
this.chartView = !(bool);
}
tableView: true,
chartView: false
}
MyObject.view(false)
console.log(MyObject.tableView); // Outputs false
console.log(MyObject.chartView) // Outputs true
This way you will always have opposite of tableView in chartView with a single function call.
I have a static javascript function that can take 1, 2 or 3 parameters:
function getData(id, parameters, callback) //parameters (associative array) and callback (function) are optional
I know I can always test if a given parameter is undefined, but how would I know if what was passed was the parameter or the callback?
What's the best way of doing this?
Examples of what could be passed in:
1:
getData('offers');
2:
var array = new Array();
array['type']='lalal';
getData('offers',array);
3:
var foo = function (){...}
getData('offers',foo);
4:
getData('offers',array,foo);
You can know how many arguments were passed to your function and you can check if your second argument is a function or not:
function getData (id, parameters, callback) {
if (arguments.length == 2) { // if only two arguments were supplied
if (Object.prototype.toString.call(parameters) == "[object Function]") {
callback = parameters;
}
}
//...
}
You can also use the arguments object in this way:
function getData (/*id, parameters, callback*/) {
var id = arguments[0], parameters, callback;
if (arguments.length == 2) { // only two arguments supplied
if (Object.prototype.toString.call(arguments[1]) == "[object Function]") {
callback = arguments[1]; // if is a function, set as 'callback'
} else {
parameters = arguments[1]; // if not a function, set as 'parameters'
}
} else if (arguments.length == 3) { // three arguments supplied
parameters = arguments[1];
callback = arguments[2];
}
//...
}
If you are interested, give a look to this article by John Resig, about a technique to simulate method overloading on JavaScript.
Er - that would imply that you are invoking your function with arguments which aren't in the proper order... which I would not recommend.
I would recommend instead feeding an object to your function like so:
function getData( props ) {
props = props || {};
props.params = props.params || {};
props.id = props.id || 1;
props.callback = props.callback || function(){};
alert( props.callback )
};
getData( {
id: 3,
callback: function(){ alert('hi'); }
} );
Benefits:
you don't have to account for argument order
you don't have to do type checking
it's easier to define default values because no type checking is necessary
less headaches. imagine if you added a fourth argument, you'd have to update your type checking every single time, and what if the fourth or consecutive are also functions?
Drawbacks:
time to refactor code
If you have no choice, you could use a function to detect whether an object is indeed a function ( see last example ).
Note: This is the proper way to detect a function:
function isFunction(obj) {
return Object.prototype.toString.call(obj) === "[object Function]";
}
isFunction( function(){} )
You should check type of received parameters. Maybe you should use arguments array since second parameter can sometimes be 'parameters' and sometimes 'callback' and naming it parameters might be misleading.
I know this is a pretty old question, but I dealt with this recently. Let me know what you think of this solution.
I created a utility that lets me strongly type arguments and let them be optional. You basically wrap your function in a proxy. If you skip an argument, it's undefined. It may get quirky if you have multiple optional arguments with the same type right next to each other. (There are options to pass functions instead of types to do custom argument checks, as well as specifying default values for each parameter.)
This is what the implementation looks like:
function displayOverlay(/*message, timeout, callback*/) {
return arrangeArgs(arguments, String, Number, Function,
function(message, timeout, callback) {
/* ... your code ... */
});
};
For clarity, here is what is going on:
function displayOverlay(/*message, timeout, callback*/) {
//arrangeArgs is the proxy
return arrangeArgs(
//first pass in the original arguments
arguments,
//then pass in the type for each argument
String, Number, Function,
//lastly, pass in your function and the proxy will do the rest!
function(message, timeout, callback) {
//debug output of each argument to verify it's working
console.log("message", message, "timeout", timeout, "callback", callback);
/* ... your code ... */
}
);
};
You can view the arrangeArgs proxy code in my GitHub repository here:
https://github.com/joelvh/Sysmo.js/blob/master/sysmo.js
Here is the utility function with some comments copied from the repository:
/*
****** Overview ******
*
* Strongly type a function's arguments to allow for any arguments to be optional.
*
* Other resources:
* http://ejohn.org/blog/javascript-method-overloading/
*
****** Example implementation ******
*
* //all args are optional... will display overlay with default settings
* var displayOverlay = function() {
* return Sysmo.optionalArgs(arguments,
* String, [Number, false, 0], Function,
* function(message, timeout, callback) {
* var overlay = new Overlay(message);
* overlay.timeout = timeout;
* overlay.display({onDisplayed: callback});
* });
* }
*
****** Example function call ******
*
* //the window.alert() function is the callback, message and timeout are not defined.
* displayOverlay(alert);
*
* //displays the overlay after 500 miliseconds, then alerts... message is not defined.
* displayOverlay(500, alert);
*
****** Setup ******
*
* arguments = the original arguments to the function defined in your javascript API.
* config = describe the argument type
* - Class - specify the type (e.g. String, Number, Function, Array)
* - [Class/function, boolean, default] - pass an array where the first value is a class or a function...
* The "boolean" indicates if the first value should be treated as a function.
* The "default" is an optional default value to use instead of undefined.
*
*/
arrangeArgs: function (/* arguments, config1 [, config2] , callback */) {
//config format: [String, false, ''], [Number, false, 0], [Function, false, function(){}]
//config doesn't need a default value.
//config can also be classes instead of an array if not required and no default value.
var configs = Sysmo.makeArray(arguments),
values = Sysmo.makeArray(configs.shift()),
callback = configs.pop(),
args = [],
done = function() {
//add the proper number of arguments before adding remaining values
if (!args.length) {
args = Array(configs.length);
}
//fire callback with args and remaining values concatenated
return callback.apply(null, args.concat(values));
};
//if there are not values to process, just fire callback
if (!values.length) {
return done();
}
//loop through configs to create more easily readable objects
for (var i = 0; i < configs.length; i++) {
var config = configs[i];
//make sure there's a value
if (values.length) {
//type or validator function
var fn = config[0] || config,
//if config[1] is true, use fn as validator,
//otherwise create a validator from a closure to preserve fn for later use
validate = (config[1]) ? fn : function(value) {
return value.constructor === fn;
};
//see if arg value matches config
if (validate(values[0])) {
args.push(values.shift());
continue;
}
}
//add a default value if there is no value in the original args
//or if the type didn't match
args.push(config[2]);
}
return done();
}
I recommend you to use ArgueJS.
You can just type your function this way:
function getData(){
arguments = __({id: String, parameters: [Object], callback: [Function]})
// and now access your arguments by arguments.id,
// arguments.parameters and arguments.callback
}
I considered by your examples that you want your id parameter to be a string, right?
Now, getData is requiring a String id and is accepting the optionals Object parameters and Function callback. All the use cases you posted will work as expected.
So use the typeof operator to determine if the second parameter is an Array or function.
This can give some suggestions:
https://planetpdf.com/testing-for-object-types-in-javascript/
I am not certain if this is work or homework, so I don't want to give you the answer at the moment, but the typeof will help you determine it.
Are you saying you can have calls like these:
getData(id, parameters);
getData(id, callback)?
In this case you can't obviously rely on position and you have to rely on analysing the type:
getType() and then if necessary getTypeName()
Check if the parameter in question is an array or a function.
You can use the arguments object property inside the function.
I think you want to use typeof() here:
function f(id, parameters, callback) {
console.log(typeof(parameters)+" "+typeof(callback));
}
f("hi", {"a":"boo"}, f); //prints "object function"
f("hi", f, {"a":"boo"}); //prints "function object"
If your problem is only with function overloading (you need to check if 'parameters' parameter is 'parameters' and not 'callback'), i would recommend you don't bother about argument type and
use this approach. The idea is simple - use literal objects to combine your parameters:
function getData(id, opt){
var data = voodooMagic(id, opt.parameters);
if (opt.callback!=undefined)
opt.callback.call(data);
return data;
}
getData(5, {parameters: "1,2,3", callback:
function(){for (i=0;i<=1;i--)alert("FAIL!");}
});
This I guess may be self explanatory example:
function clickOn(elem /*bubble, cancelable*/) {
var bubble = (arguments.length > 1) ? arguments[1] : true;
var cancelable = (arguments.length == 3) ? arguments[2] : true;
var cle = document.createEvent("MouseEvent");
cle.initEvent("click", bubble, cancelable);
elem.dispatchEvent(cle);
}
Can you override the function? Will this not work:
function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}