One more question regarding arguments in javascript. I was reading a tutorial about the javascript framework Prototype and came over the following text, I will quote below:
"If you have some DOM methods of your own that you'd like to add to those of Prototype, no problem! Prototype provides a mechanism for this, too. Suppose you have a bunch of functions encapsulated in an object, just pass the object over to Element.addMethods():
var MyUtils = {
truncate: function(element, length){
element = $(element);
return element.update(element.innerHTML.truncate(length));
},
updateAndMark: function(element, html){
return $(element).update(html).addClassName('updated');
}
}
Element.addMethods(MyUtils);
// now you can:
$('explanation').truncate(100);
The only thing to watch out here is to make sure the first argument of these methods is the element itself. In your methods, you can also return the element in the end to allow for chainability (or, as practiced in the example, any method which itself returns the element)."
Everything seems pretty straightforward except one thing that I seem to have problem understanding. It says that you need to make sure the first argument of these methods is the element itself. And that is the case in the functions but when you call the function you only provide 1 argument? So for example:
$('explanation').truncate(100);
There is only 1 argument here that is sent to the function namely 100, but the function signature looks like this:
truncate: function(element, length){
So element comes from where? And is it logic that you must make sure that the first argument is the element itself but you do not provide it when you call the function?
Prototype provides two ways to do things:
Methods on the object itself, for example:
Element.toggleClassName('selector', 'something');
Via the prototype:
$('selector').toggleClassName('something');
IIRC, the second method simply calls the first, passing the Element selected to it as well as the original argument. That's what happens in your case as well.
Javascript functions can test their arguments and decide what to do based on what was passed. jQuery is famous for this and it's fairly common in a lot of libraries. It allows one named function to perform many different (but related) operations and often allows shorthand notation that can omit optional or default parameters. You can often pass one, two or three arguments and the code inside the function will examine which arguments are present and, in some cases, the type of the arguments to decide how to treat them.
When functions do this, you have to read their documentation carefully to see which parameters are required and which are optional and what order things can be passed in.
If a function optionally takes three parameters and all three are different types, it's possible for the function to examine the type of each passed parameter and accept them in any order. It can tell which is which by the type. I don't personally think this is good programming style, but it is possible. More likely, it allows you to just leave out some parameters which are not needed and the function will either adapt it's behavior when some things are missing or will set a default for that value.
In your specific example of truncate, I'd have to see a documentation page for that function to explain what it does when no element is passed vs. when one is.
Here's a made up example of a string truncate function that can be used a bunch of different ways:
var str = "This is a long string that I want to be truncated to something shorter";
str.truncate(10); // truncate to max length of 10
str.truncate("string", "end"); // truncate after the first occurrence of "string"
str.truncate(/\bbe\b/, "begin"); // truncate before the first occurrence of a regular expression
str.truncate("string", 10); // truncate 10 chars after the first occurrence of "string"
And the outline of this function would look something like this:
string.prototype.truncate = function(lookFor, pos, cnt) {
if (lookFor == undefined) {
return(this); // no parameters passed, nothing to do
}
if (typeof lookFor == "Number") {
// truncate to a certain length
} else if (typeof lookFor == "String") {
// look for a plain string
} else if (lookFor.exec) {
// look for a regular expression
} else {
return(this); // nothing to do
}
}
Related
Coming from Python into some JavaScript-based APIs I'm confused by some of the syntax. And I can't find an answer in all of the noise of random information about declaring functions.
In Python, you can mix specifying arguments to a function base on the order and based on the name:
np.arange(1,5,step = 5)
Can you do something like that in Javascript?
If there is a function like:
ee.List.sequence(start,end, step, count)
and it only needs three out of the four arguments I can really easily specify the start, end, step, like so:
ee.List.sequence(1,100,2)
But, do I have to use the object notation to specify the count?
ee.List.sequence({start=1,end=100, count=50})
Is there a shorthand, like in Python, such as:
ee.List.sequence(1,100,{count=50})
or
ee.List.sequence(1,100,,50)?
It seems that what you are really asking is less about JavaScript as a language and more about specific APIs. So, here's some things to know:
In JavaScript, all arguments are optional. In other words, there is no way to enforce that a function is called with the proper amount or order of arguments. It's up to the caller to know the signature of the function its calling and call it appropriately. It's also up to the creator of the function to be prepared for some or all of the arguments to not be passed. There is an arguments array-like object that all functions have that can assist with this, but checking the inputs is also pretty easy. Here's an example:
// Here's an example of a function that does not explicitly declare any arguments
function foo1(){
// However, arguments might still be passed and they can be accessed
// through the arguments object:
console.log("Arguments.length = ", arguments.length);
console.log(arguments);
}
foo1("test", "boo!"); // Call the function and pass args even though it doesn't want any
// ***********************************************
// Here's an example of a function that needs the first arg to work,
// but the seond one is optional
function foo2(x, y){
if(y){
console.log(x + y);
} else {
console.log(x);
}
}
foo2(3);
foo2(4, 5);
In JavaScript, your functions can take any valid primitive or object. Again, it's up to the caller to know what the API is and call it correctly:
function foo1(string1, number1, object1, string2){
console.log(arguments);
}
foo1("test", 3.14, {val:"John Doe"}, "ing");
I've found the answer in this JavaScript tutorial.
In general, yes, the default JavaScript function can take anything as an argument. But any well-written API function, with specified arguments (and default values), will not allow this to happen.
So the two options are
Supply the arguments in order, without naming them.
`ee.List.sequence(1,100,2)`
Pass them in a named object (where suddenly order doesn't matter)
`ee.List.sequence({start=1,end=100, count=50})`
There is no mixed notation like Python has
ee.List.sequence(1,100,{count=50})
BUT there is a workaround for 1
In case you want to use a different combination of arguments, one can supply null values to the arguments that are omitted. So the following can be used:
`ee.List.sequence(1,100,null,50)`
With array destructuring, it's possible to discard leading items by inserting commas without a preceding reference:
const [ , two ] = [ 1, 2 ]
The same isn't true of function signatures — the following code won't parse because the leading comma in the signature is unexpected:
function ditchFirstArgument( , second ){}
Why do I need to provide references for leading parameters in ES6 function expressions?
Roman's insight from Go is useful but inappropriate to JS where the token _ is a valid reference, conventionally used by the Underscore and later Lodash libraries.
Even if that's acceptable, you'd have to create and avoid dud references for every unused argument, which isn't ideal.
However, it is possible to destructure a function argument into an empty object, which effectively nullifies the parameter without reference.
function take_third( {}, {}, third ){
return third
}
EDIT: As Paul points out in the comments, this will throw if any of the skipped parameter values are null or undefined. undefined values can be guarded against with default assignments, but this won't work for null:
function take_third( {} = {}, {} = {}, third ){
return third
}
Why do I need to provide references for leading parameters in ES6 function expressions?
Because otherwise it would be a syntax error. In not just ES6 but any version of the language you cannot elide formal parameters because the spec does not provide for it.
If you really want to do this (but why?), you could write it as
function ditchFirstArgument(...[, second]) {}
or at least you will be able to in some future version of ES; see https://github.com/tc39/ecma262/commit/d322357e6be95bc4bd3e03f5944a736aac55fa50. This already seems to be supported in Chrome. Meanwhile, the best you can do is
function ditchFirstArgument(...args) {
const [, second] = args;
But why does the spec not allow elision of parameters?
You'd have to ask the people who wrote it, but they may have never even considered it, or if they did, rejected it because it's bug-prone, hardly ever necessary, and can easily be worked around using dummy formal parameters like _.
I believe it's a common pattern to name unused variables with an underscore:
function ditchFirstArgument(_, second) { /* ... */ }
While it would not prevent you from actually using this variable (like in Go), it seems to be a pretty straightforward workaround.
I'm creating a JS library object that has a primary function, but I also want to assign sub-methods to that primary function to extend it.
So right now what I have looks like this:
Parser.prototype = {
init: function(){
},
primaryFunction:function(){
}
}
I really like this notation for assigning methods to Parser.prototype, but how can I use similar notation for assigning methods to primaryFunction? Also, the submethods are characterized by a string. Because eventually, my goal is to be able call like this: primaryFunction["*"]();
It seems I can't do this:
primaryFunction:function(char){
"*":function(){
}
}
Sort of makes sense why, but am I forced to do this?
primaryFunction:function(char){
this["*"] = function(){};
}
There's a big difference between adding properties to Parser.prototype and adding properties to primaryFunction. When you add to Parser.prototype, you're adding properties to a non-function object that is used as the underlying prototype of any object (instance) created via the new Parser expression. But if you add properties to primaryFunction, you're adding those properties to the function object of primaryFunction, directly.
The result is that properties added to Parser.prototype become, in effect, methods of instances created via new Parser. Within calls to those "methods," provided they're made normally (instance.init() and similar), this will refer to the instance.
But properties added to primaryFunction are methods of the function primaryFunction, not instances created with it. If you call them in the normal way (instance.primaryFunction["*"]()), within the call this is primaryFunction (the function object), not instance.
If that's really want you want, then you simply assign them after the fact (you can't do it within the object initializer):
Parser.prototype.primaryFunction["*"] = function() { /* ... */ };
But there are relatively few use cases for doing that, not least because if you're using Parser as a constructor function (e.g., with new Parser), you probably want to do things with the instance created, and you won't have access to it in primaryFunction["*"] unless you do some funny stuff.
The funny stuff, FWIW, looks like this — but I'm not recommending it (nor recommending against it), just being complete:
/* ...inside the `Parser` function... */
this.primaryFunction["*"] = this.primaryFunction["*"].bind(this);
That creates a new function that, when called, will call primaryFunction but setting this to the instance created by new Parser, and saves the resulting function as an "own" property on the instance being created.
From your comment below:
So my use case is just a parsing object that performs different
actions based on what characters it reads. The Parser loops through a
string, and my goal is to call a different function based on what the
current character at the index is. There will only be a set of unique
functions for each special character that may be in the string. For
instance, those characters could be: *, /, \, <, >,
essentially non alphanumerics. For anything alphanumeric, I want a
default function to handle those. So essentially, in a loop I'm
getting the currentChar, and trying to call a sub-method based on
which char it is. So the loop is essentially doing:
this.handleChar[currentChar]().
The bind solution above would work for that. But I think I'd probably go another way: Have a private, shared map of handler functions that you call (either passing in the parser instance as an argument, or as this) with the characters:
var Parser = function() {
var handlers = {
"*": function(char) {
// handle *...
},
"/": function(char) {
// handle /...
},
// ...and so on...
"default": function(char) {
// default handling...
}
};
function Parser() {
// ...
}
// (I always advocate *augmenting*, not replacing, `FuncName.prototype` properties)
Parser.prototype.handleChar = function() {
var currentChar;
while (!!(currentChar = /*...get the character...*/)) {
(handlers[currentChar] || handlers.default).call(this, currentChar);
// On some older JavaScript engines, you'd have to write the above like this:
//
// (handlers[currentChar] || handlers["default"]).call(this, currentChar);
//
// ...because ES3 didn't let you use a keyword as a property name literal
// (ES5 does, and some engines always did).
}
};
return Parser;
}();
That example passes the parser instance as this by using Function#call (the first argument is what ends up being this in the function call). It also uses JavaScript's curiously-powerful || operator to pick the function on handlers to call.
(If you don't care that handlers be private, then you don't need the wrapper.)
I've always passed arguments to a function like so:
setValue('foo','#bar')
function setValue(val,ele){
$(ele).val(val);
};
Forgive the silly example. But recently I have been working on a project that has some functions that take a lot of arguments. So I started passing the arguments through as an object (not sure if that's the correct way to put that), like so:
setValue({
val:'foo',
ele:'#bar'
});
And then in the function:
function setValue(options){
var value = options.val;
var element = options.ele;
$(element).val(value);
};
My question is, is there a better way to do that? Is it common practice (or okay) to call these 'options'? And do you typically need to 'unpack' (for lack of a better term) the options and set local vars inside the function? I have been doing it this way in case one of them was not defined.
I'm really looking to not create bad habits and write a bunch of code that is ugly. Any help is appreciated and + by me. Thanks.
I do the exact same thing, except I don't declare a new variable for each option inside the function.
I think options is a good name for it although I shorten it to opts.
I always have a "default" object within the function that specify default values for each available option, even if its simply null. I use jQuery, so I can just use $.extend to merge the defaults and user-specified options like this: var opts = $.extend({}, defaults, opts);
I believe this is a great pattern. I've heard an options object like this referred to as a "builder object" in other languages (at least in the context of object creation). Here are some of the advantages:
Users of your function don't have to worry about what order the parameters are in. This is especially helpful in cases like yours where the method takes a lot of arguments. It's easy to get those mixed up, and JavaScript will not complain!
It's easy to make certain parameters optional (this comes in handy when writing a plugin or utility).
There are some pitfalls though. Specifically, the user of your function could not specify some of the options and your code would choke (note that this could also happen with a normal JS function: the user still doesn't have to supply the correct arguments). A good way for handling this is to provide default values for parameters that are not required:
var value = options.val || 0;
var element = options.ele || {};
$(element).val(value);
You could also return from the function immediately or throw an exception if the correct arguments aren't supplied.
A good resource for learning how to handle builder objects is to check out the source of things like jQueryUI.
I realize this question is a year old, but I think the cleanest way to pass an arbitrary number of arguments to a JavaScript function is using an array and the built in apply method:
fun.apply(object, [argsArray])
Where fun is the function, object is your scope/context in which you want the function to be executed and the argsArray is an array of the arguments (which can hold any number of arguments to be passed.
The current pitfall right now is that the arguments must be an array (literal or object) and not an array-like object such as {'arg' : 6, 'arg2' : "stuff"}. ECMAScript 5 will let you pass array-like objects, but it only seems to work in FireFox at the moment and not IE9 or Chrome.
If you look at the jQuery implementation, it uses an options class to handle most of the arbitrary-number-of-parameters functions, so I think you are in good company.
The other way is to test for arguments.length, but that only works if your arguments are always in the same order of optionality.
It's worth remembering that all functions have a bonus parameter called arguments that is an object very much like a JS array (it has length but none of the array functions) that contains all the parameters passed in.
Useful if you want to pass in a range of parameters (e.g.
function Sum() {
var i, sum = 0;
for (i=0; i < arguments.length; i++){
sum+=arguments[i];
}
return sum;
};
If this isn't the case and you just have a lot of parameters, use the params object as you've described.
Nothing wrong with that practice.
"Options" seems like as good a name as any.
You don't need to "unpack" them, but if you'll be accessing the same item several times, it will be a little more efficient to reference them in local variables because local variable access is generally quicker than property lookups.
I have run across code that invokes a function as fn(a,b,c) but the definition of fn is fn(a,b) and then inside the author invokes arguments[2] which would imply a third undeclared argument. Is this legit? (I am new to the site and tried to search for a related question before posting, but was unable to find one. If there is a custom for doing so, I would love to be educated.) Thanks.
It's allowed. It's usually better to specify good argument names and then check if they are null or not, for readability and sanity. People reading your code won't expect or understand that technique.
There are cases where it acceptable... for example:
function add(){
var sum = 0;
for (var i = 0; i < arguments.length; i++){
sum += arguments[i];
}
return sum;
}
However, even in this case it would be better to add placeholder variable names for the sake of readers:
function add(val1, val2, etc){
var sum = 0;
for (var i = 0; i < arguments.length; i++){
sum += arguments[i];
}
return sum;
}
Legal. Ugly, but legal.
Its ugly, BUT is also used in many javascript frameworks, such as jQuery. There are obvious advantages to using it for some purposes, but I'd follow these general rules:
don't use it simply because some (known) arguments are optional. Instead, name the arguments (or take an object as an argument instead) and check the arguments (or object's properties) explicitly for null or undefined
if the method is something that could potentially take an unknown/infinite number of arguments, it would make sense to use this approach, for example if you were for some reason creating a custom concat() method, you might want to allow any number of arguments
if you do use it, comment any parts of the code that may be confusing to follow, in particular, you'd want to comment/document how the function should be called
Another alternative might be to pass an array as a single argument and iterate over its contents or an object (name value pairs) passing arguments of interest and ignoring others. I prefer to pass an object.
It's legal, but I would avoid it as a matter of style. It's usually a better idea to declare the argument - by doing so, you make the meaning of the function more obvious. One can just look at the function name and argument list to (hopefully) get an idea of the function's purpose.
I'd reserve usage of the arguments array for completely variable-length argument lists, not just a single optional parameter.