how do I compare 2 functions in javascript - javascript

How do I compare 2 functions in javascript?
I am not talking about internal reference. Say
var a = function(){return 1;};
var b = function(){return 1;};
Is it possible to compare a and b ?

var a = b = function( c ){ return c; };
//here, you can use a === b because they're pointing to the same memory and they're the same type
var a = function( c ){ return c; },
b = function( c ){ return c; };
//here you can use that byte-saver Andy E used (which is implicitly converting the function to it's body's text as a String),
''+a == ''+b.
//this is the gist of what is happening behind the scences:
a.toString( ) == b.toString( )

Closures mean that you need to be very careful what you mean when you say "compare". For example:
function closure( v ) { return function(){return v} };
a = closure('a'); b = closure('b');
[a(), b()]; // ["a", "b"]
// Now, are a and b the same function?
// In one sense they're the same:
a.toString() === b.toString(); // true, the same code
// In another sense they're different:
a() === b(); // false, different answers
// In a third sense even that's not enough:
a2 = closure('a');
a() === a2(); // true
a === a2; // false, not the same object
The ability to reach outside the function means that in a general sense, comparing functions is impossible.
However, in a practical sense you can get a very long way with Javascript parsing libraries like Esprima or Acorn. These let you build up an "Abstract Syntax Tree" (AST), which is a JSON description of your program. For example, the ast your return 1 functions looks like this
ast = acorn.parse('return 1', {allowReturnOutsideFunction:true});
console.log( JSON.stringify(ast), null, 2)
{
"body": [
{
"argument": {
"value": 1, // <- the 1 in 'return 1'
"raw": "1",
"type": "Literal"
},
"type": "ReturnStatement" // <- the 'return' in 'return 1'
}
],
"type": "Program"
}
// Elided for clarity - you don't care about source positions
The AST has all the information you need to make comparisons - it is the Javascript function in data form. You could normalize variable names, check for closures, ignore dates and so on depending on your needs.
There are a bunch of tools and libraries to help simplify the process but even so, it's likely to be a lot of work and probably not practical, but it is mostly possible.

You can compare two variables that might contain function references to see if they refer to the exact same function, but you cannot really compare two separate functions to see if they do the same thing.
For example, you can do this:
function foo() {
return 1;
}
var a = foo;
var b = foo;
a == b; // true
But, you can't reliably do this:
function foo1() {
return 1;
}
function foo2() {
return 1;
}
var a = foo1;
var b = foo2;
a == b; // false
You can see this second one here: http://jsfiddle.net/jfriend00/SdKsu/
There are some circumstances where you can use the .toString() operator on functions, but that's comparing a literal string conversion of your function to one another which, if even off by a teeny bit that is inconsequential to what it actually produces, will not work. I can think of no situation where I would recommend this as a reliable comparison mechanism. If you were seriously thinking about doing it this way, I'd ask why? What are you really trying to accomplish and try to find a more robust way of solving the problem.

toString() on a function returns the exact declaration. You can modify jfriend00's code to test it out.
This means you can test to see if your functions are exactly the same, including what spaces and newlines you put in it.
But first you have to eliminate the difference in their names.
function foo1() {
return 1;
}
function foo2() {
return 1;
}
//Get a string of the function declaration exactly as it was written.
var a = foo1.toString();
var b = foo2.toString();
//Cut out everything before the curly brace.
a = a.substring(a.indexOf("{"));
b = b.substring(b.indexOf("{"));
//a and b are now this string:
//"{
// return 1;
//}"
alert(a == b); //true.
As the others said, this is unreliable because a single whitespace of difference makes the comparison false.
But what if you're employing it as a protective measure? ("Has someone altered my function since I created it?") You may actually desire this kind of strict comparison then.

ES6+ clean solution using template literals:
const fn1 = () => {}
const fn2 = () => {}
console.log(`${fn1}` === `${fn2}`) // true
Basically means:
console.log(fn1.toString() === fn2.toString()) // true

Convert function to string, then, replace line-break and space before comparing:
let a = function () {
return 1
};
let b = function () {
return 1
};
a = a.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(a); // 'function () { return 1}'
console.log(b); // 'function () { return 1}'
console.log(a === b); // true
b = function () {
return 2
};
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(b); // 'function () { return 2}'
console.log(a === b); // false
b = () => 3;
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(b); // '() => 3'
console.log(a === b); // false
p/s: If you are using ES6, try to use let instead of var.

Related

Performing assignment operations on an object

Example JSFiddle so you can get a better idea of what is going on http://jsfiddle.net/brsXL/3/ (open your console and view the logged vars object).
I am building a parser and computer for a specific subset of maths in JavaScript, it takes an expression as a string from the user and allows them to use variables. To keep my computational logic simple but allow for the use of variables I have created an object that acts like a number but has the bonus of being passed by reference.
var Variable = function(value) {
this.value = value || null;
}
Variable.prototype.valueOf = function() {
return this.value;
}
This works so that:
var a = new Variable(10);
console.log(a + 2); // = 12
console.log(a / 2); // = 5
However as soon as I wish to perform any form of assignment operation such as += the object is lost and gets replaced by the result of the operation against the object's value property. e.g.
var a = new Variable(10);
console.log(a += 2); // = 12
console.log(a); // = 12
The reason I need it to work like this is because I want to use the same function to handle both numbers and variables. I could add code to each assignment operation but this feels sub-optimal to me, e.g.
var ops = {
"+=" : function(a, b) {
if (a instanceof Variable) {
a.value += b;
} else {
a += b;
}
return a;
},
...
}
But I'd much rather write:
var ops = {
"+=" : function(a, b) {
return a += b;
},
...
}
Can this be done?
I'd much rather write:
function(a, b) {
return a += b;
}
Can this be done?
No. It's impossible to pass a Reference value as a single variable. You always will need to use object properties. a is always local-scoped in your function, so changing it won't affect the outer world. And I'd discourage you from trying to make your operators functions that operate on higher-scope variables…
I think in your case it's quite fine to use an explicit test for variables, because the assignment operator has to do that actually. You cannot assign to literals or other values, only to variables. It might even be
var ops = {
"=" : function(a, b) {
if (a instanceof Variable) {
a.value = +b; // cast b to a number (from whatever it is)
return a;
} else {
throw new Error("Invalid assignment to non-variable "+a);
}
},
...
}
Also, to avoid code duplication you might not write out all the compound assignment operators. Define them in a generic way:
["+", "-", "*", "/"].forEach(function(op) {
ops[op+"="] = function(a, b) {
return ops["="].call(this, a, ops[op].call(this, a, b));
};
});
(Updated jsfiddle demo)

Set undefined parameters via arguments

I'm trying to write a function that corrects the arguments of a function based on previously specified optional parameters. I've come to a problem though. It seems that I can't set variables via the arguments array unless they have been defined in any way before. The code below shows an example of the problem I'm facing.
function foo(a, b, c) {
arguments[0] = "lorem";
arguments[1] = "ipsum";
arguments[2] = "dolor";
console.log([a, b, c]);
}
foo(null); // ["lorem", undefined, undefined]
foo(null, null); // ["lorem", "ipsum", undefined]
foo(null, null, null); // ["lorem", "ipsum", "dolor"]
When logging arguments the result is always ["lorem", "ipsum", "dolor"] though.
Is there any way to solve this problem ?
I can't set a, b and c directly because a function called in foo wouldn't have access to these names.
My goal would look like something like this:
function foo(a, b, c) {
var rules = [];
// Rule for optional parameter 1 (b)
// If it equals true the value of b is shifted to the next parameter (c)
rules[1] = function(val) { return val !== "ipsum"; };
optionalize(rules);
console.log([a, b, c]);
}
foo("lorem", "dolor"); // ["lorem", undefined, "dolor"];
The arguments array isn't really an array but an "array-like" object. You can't change its length.
What you try to do is usually done using
a = a || "lorem";
or, if you don't want to replace any "falsy" argument, using
if (typeof a === "undefined") a = "lorem";
It is a bit peculiar, but is this what you had in mind?
function optionalize(fn, options) {
var i, key, rule;
for(i = 0; i < options.length; i += 1) {
key = fn.placement[i];
rule = fn.ruleset[key];
// If the rule exists and returns true, shift value to the right.
if(rule && rule(options[i])) {
options[i+1] = options[i];
options[i] = undefined;
}
// Assign the numeric index to an alphabet key.
// Depending on your use case, you may want to substitute a plain Object here and return that, instead of adding non-numeric properties to an Array.
options[key] = options[i];
}
}
// Test function
function foo(a, opts) {
opts = opts || [];
optionalize(foo, opts);
console.log([a, opts.b, opts.c]);
}
// Optional argument names, in the order that they would be received.
foo.placement = ['b', 'c'];
// Optionalize rules
foo.ruleset = {
b: function (val) { return val !== "ipsum"; }
};
// Demonstration
foo('lorem');
foo('lorem', []);
foo('lorem', ['dolor']);
foo('lorem', ['ipsum', 'dolor']);
As dystroy's answer has already indicated, the arguments variable isn't a real Array, and changing it may not be a good idea. I have provided a solution which does not rely on arguments and fulfills the criteria as far as could be possible using simple JavaScript.
The function foo is specified with a required argument a, followed by an Array of optional arguments named opts. An optionalize specification is set onto foo, through the placement and ruleset properties. The optionalize function takes this information and transforms the array's indices into usable name keys, applying the rules as necessary.
I'm not sure what you're trying to do but something like arguments[0] = a ? a : "lorem" and so on ?
You can convert arguments to an array
function foo () {
var arguments = Array.prototype.slice.call(arguments);
arguments.push(4);
console.log(arguments);
}
foo(1,2,3);
​

Set a default parameter value for a JavaScript function

I would like a JavaScript function to have optional arguments which I set a default on, which get used if the value isn't defined (and ignored if the value is passed). In Ruby you can do it like this:
def read_file(file, delete_after = false)
# code
end
Does this work in JavaScript?
function read_file(file, delete_after = false) {
// Code
}
From ES6/ES2015, default parameters are in the language specification.
function read_file(file, delete_after = false) {
// Code
}
just works.
Reference: Default Parameters - MDN
Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.
In ES6, you can simulate default named parameters via destructuring:
// the `= {}` below lets you call the function without any parameters
function myFor({ start = 5, end = 1, step = -1 } = {}) { // (A)
// Use the variables `start`, `end` and `step` here
···
}
// sample call using an object
myFor({ start: 3, end: 0 });
// also OK
myFor();
myFor({});
Pre ES2015,
There are a lot of ways, but this is my preferred method — it lets you pass in anything you want, including false or null. (typeof null == "object")
function foo(a, b) {
a = typeof a !== 'undefined' ? a : 42;
b = typeof b !== 'undefined' ? b : 'default_b';
...
}
function read_file(file, delete_after) {
delete_after = delete_after || "my default here";
//rest of code
}
This assigns to delete_after the value of delete_after if it is not a falsey value otherwise it assigns the string "my default here". For more detail, check out Doug Crockford's survey of the language and check out the section on Operators.
This approach does not work if you want to pass in a falsey value i.e. false, null, undefined, 0 or "". If you require falsey values to be passed in you would need to use the method in Tom Ritter's answer.
When dealing with a number of parameters to a function, it is often useful to allow the consumer to pass the parameter arguments in an object and then merge these values with an object that contains the default values for the function
function read_file(values) {
values = merge({
delete_after : "my default here"
}, values || {});
// rest of code
}
// simple implementation based on $.extend() from jQuery
function merge() {
var obj, name, copy,
target = arguments[0] || {},
i = 1,
length = arguments.length;
for (; i < length; i++) {
if ((obj = arguments[i]) != null) {
for (name in obj) {
copy = obj[name];
if (target === copy) {
continue;
}
else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
};
to use
// will use the default delete_after value
read_file({ file: "my file" });
// will override default delete_after value
read_file({ file: "my file", delete_after: "my value" });
I find something simple like this to be much more concise and readable personally.
function pick(arg, def) {
return (typeof arg == 'undefined' ? def : arg);
}
function myFunc(x) {
x = pick(x, 'my default');
}
In ECMAScript 6 you will actually be able to write exactly what you have:
function read_file(file, delete_after = false) {
// Code
}
This will set delete_after to false if it s not present or undefined. You can use ES6 features like this one today with transpilers such as Babel.
See the MDN article for more information.
Default Parameter Values
With ES6, you can do perhaps one of the most common idioms in JavaScript relates to setting a default value for a function parameter. The way we’ve done this for years should look quite familiar:
function foo(x,y) {
x = x || 11;
y = y || 31;
console.log( x + y );
}
foo(); // 42
foo( 5, 6 ); // 11
foo( 5 ); // 36
foo( null, 6 ); // 17
This pattern is most used, but is dangerous when we pass values like
foo(0, 42)
foo( 0, 42 ); // 53 <-- Oops, not 42
Why? Because the 0 is falsy, and so the x || 11 results in 11, not the directly passed in 0. To fix this gotcha, some people will instead write the check more verbosely like this:
function foo(x,y) {
x = (x !== undefined) ? x : 11;
y = (y !== undefined) ? y : 31;
console.log( x + y );
}
foo( 0, 42 ); // 42
foo( undefined, 6 ); // 17
we can now examine a nice helpful syntax added as of ES6 to streamline the assignment of default values to missing arguments:
function foo(x = 11, y = 31) {
console.log( x + y );
}
foo(); // 42
foo( 5, 6 ); // 11
foo( 0, 42 ); // 42
foo( 5 ); // 36
foo( 5, undefined ); // 36 <-- `undefined` is missing
foo( 5, null ); // 5 <-- null coerces to `0`
foo( undefined, 6 ); // 17 <-- `undefined` is missing
foo( null, 6 ); // 6 <-- null coerces to `0`
x = 11 in a function declaration is more like x !== undefined ? x : 11 than the much more common idiom x || 11
Default Value Expressions
Function default values can be more than just simple values like 31; they can be any valid expression, even a function call:
function bar(val) {
console.log( "bar called!" );
return y + val;
}
function foo(x = y + 3, z = bar( x )) {
console.log( x, z );
}
var y = 5;
foo(); // "bar called"
// 8 13
foo( 10 ); // "bar called"
// 10 15
y = 6;
foo( undefined, 10 ); // 9 10
As you can see, the default value expressions are lazily evaluated, meaning they’re only run if and when they’re needed — that is, when a parameter’s argument is omitted or is undefined.
A default value expression can even be an inline function expression call — commonly referred to as an Immediately Invoked Function Expression (IIFE):
function foo( x =
(function(v){ return v + 11; })( 31 )
) {
console.log( x );
}
foo(); // 42
that solution is work for me in js:
function read_file(file, delete_after) {
delete_after = delete_after || false;
// Code
}
I would highly recommend extreme caution when using default parameter values in javascript. It often creates bugs when used in conjunction with higher order functions like forEach, map, and reduce. For example, consider this line of code:
['1', '2', '3'].map(parseInt); // [1, NaN, NaN]
parseInt has an optional second parameter function parseInt(s, [radix=10]) but map calls parseInt with three arguments: (element, index, and array).
I suggest you separate your required parameters form your optional/default valued arguments. If your function takes 1,2, or 3 required parameters for which no default value makes sense, make them positional parameters to the function, any optional parameters should follow as named attributes of a single object. If your function takes 4 or more, perhaps it makes more sense to supply all arguments via attributes of a single object parameter.
In your case I would suggest you write your deleteFile function like this: (edited per instead's comments)...
// unsafe
function read_file(fileName, deleteAfter=false) {
if (deleteAfter) {
console.log(`Reading and then deleting ${fileName}`);
} else {
console.log(`Just reading ${fileName}`);
}
}
// better
function readFile(fileName, options) {
const deleteAfter = !!(options && options.deleteAfter === true);
read_file(fileName, deleteAfter);
}
console.log('unsafe...');
['log1.txt', 'log2.txt', 'log3.txt'].map(read_file);
console.log('better...');
['log1.txt', 'log2.txt', 'log3.txt'].map(readFile);
Running the above snippet illustrates the dangers lurking behind default argument values for unused parameters.
Just use an explicit comparison with undefined.
function read_file(file, delete_after)
{
if(delete_after === undefined) { delete_after = false; }
}
As an update...with ECMAScript 6 you can FINALLY set default values in function parameter declarations like so:
function f (x, y = 7, z = 42) {
return x + y + z
}
f(1) === 50
As referenced by - http://es6-features.org/#DefaultParameterValues
being a long time C++ developer (Rookie to web development :)), when I first came across this situation, I did the parameter assignment in the function definition, like it is mentioned in the question, as follows.
function myfunc(a,b=10)
But beware that it doesn't work consistently across browsers. For me it worked on chrome on my desktop, but did not work on chrome on android.
Safer option, as many have mentioned above is -
function myfunc(a,b)
{
if (typeof(b)==='undefined') b = 10;
......
}
Intention for this answer is not to repeat the same solutions, what others have already mentioned, but to inform that parameter assignment in the function definition may work on some browsers, but don't rely on it.
To anyone interested in having there code work in Microsoft Edge, do not use defaults in function parameters.
function read_file(file, delete_after = false) {
#code
}
In that example Edge will throw an error "Expecting ')'"
To get around this use
function read_file(file, delete_after) {
if(delete_after == undefined)
{
delete_after = false;
}
#code
}
As of Aug 08 2016 this is still an issue
If you are using ES6+ you can set default parameters in the following manner:
function test (foo = 1, bar = 2) {
console.log(foo, bar);
}
test(5); // foo gets overwritten, bar remains default parameter
If you need ES5 syntax you can do it in the following manner:
function test(foo, bar) {
foo = foo || 2;
bar = bar || 0;
console.log(foo, bar);
}
test(5); // foo gets overwritten, bar remains default parameter
In the above syntax the OR operator is used. The OR operator always returns the first value if this can be converted to true if not it returns the righthandside value. When the function is called with no corresponding argument the parameter variable (bar in our example) is set to undefined by the JS engine. undefined Is then converted to false and thus does the OR operator return the value 0.
function helloWorld(name, symbol = '!!!') {
name = name || 'worlds';
console.log('hello ' + name + symbol);
}
helloWorld(); // hello worlds!!!
helloWorld('john'); // hello john!!!
helloWorld('john', '(>.<)'); // hello john(>.<)
helloWorld('john', undefined); // hello john!!!
helloWorld(undefined, undefined); // hello worlds!!!
Use this if you want to use latest ECMA6 syntax:
function myFunction(someValue = "This is DEFAULT!") {
console.log("someValue --> ", someValue);
}
myFunction("Not A default value") // calling the function without default value
myFunction() // calling the function with default value
It is called default function parameters. It allows formal parameters to be initialized with default values if no value or undefined is passed.
NOTE: It wont work with Internet Explorer or older browsers.
For maximum possible compatibility use this:
function myFunction(someValue) {
someValue = (someValue === undefined) ? "This is DEFAULT!" : someValue;
console.log("someValue --> ", someValue);
}
myFunction("Not A default value") // calling the function without default value
myFunction() // calling the function with default value
Both functions have exact same behavior as each of these example rely on the fact that the parameter variable will be undefined if no parameter value was passed when calling that function.
ES6: As already mentioned in most answers, in ES6, you can simply initialise a parameter along with a value.
ES5: Most of the given answers aren't good enough for me because there are occasions where I may have to pass falsey values such as 0, null and undefined to a function. To determine if a parameter is undefined because that's the value I passed instead of undefined due to not have been defined at all I do this:
function foo (param1, param2) {
param1 = arguments.length >= 1 ? param1 : "default1";
param2 = arguments.length >= 2 ? param2 : "default2";
}
As per the syntax
function [name]([param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]]) {
statements
}
you can define the default value of formal parameters.
and also check undefined value by using typeof function.
function throwIfNoValue() {
throw new Error('Missing argument');
}
function foo(argValue = throwIfNoValue()) {
return argValue ;
}
Here foo() is a function which has a parameter named argValue. If we don’t pass anything in the function call here, then the function throwIfNoValue() will be called and the returned result will be assigned to the only argument argValue. This is how a function call can be used as a default parameter. Which makes the code more simplified and readable.
This example has been taken from here
If for some reason you are not on ES6 and are using lodash here is a concise way to default function parameters via _.defaultTo method:
var fn = function(a, b) {
a = _.defaultTo(a, 'Hi')
b = _.defaultTo(b, 'Mom!')
console.log(a, b)
}
fn() // Hi Mom!
fn(undefined, null) // Hi Mom!
fn(NaN, NaN) // Hi Mom!
fn(1) // 1 "Mom!"
fn(null, 2) // Hi 2
fn(false, false) // false false
fn(0, 2) // 0 2
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Which will set the default if the current value is NaN, null, or undefined
Yes, using default parameters is fully supported in ES6:
function read_file(file, delete_after = false) {
// Code
}
or
const read_file = (file, delete_after = false) => {
// Code
}
but prior in ES5 you could easily do this:
function read_file(file, delete_after) {
var df = delete_after || false;
// Code
}
Which means if the value is there, use the value, otherwise, use the second value after || operation which does the same thing...
Note: also there is a big difference between those if you pass a value to ES6 one even the value be falsy, that will be replaced with new value, something like null or ""... but ES5 one only will be replaced if only the passed value is truthy, that's because the way || working...
Sounds of Future
In future, you will be able to "spread" one object to another (currently as of 2019 NOT supported by Edge!) - demonstration how to use that for nice default options regardless of order:
function test(options) {
var options = {
// defaults
url: 'defaultURL',
some: 'somethingDefault',
// override with input options
...options
};
var body = document.getElementsByTagName('body')[0];
body.innerHTML += '<br>' + options.url + ' : ' + options.some;
}
test();
test({});
test({url:'myURL'});
test({some:'somethingOfMine'});
test({url:'overrideURL', some:'andSomething'});
test({url:'overrideURL', some:'andSomething', extra:'noProblem'});
MDN reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
...meanwhile what Edge DOES support is Object.assign() (IE does not, but I really hope we can leave IE behind :) )
Similarly you could do
function test(options) {
var options = Object.assign({
// defaults
url: 'defaultURL',
some: 'somethingDefault',
}, options); // override with input options
var body = document.getElementsByTagName('body')[0];
body.innerHTML += '<br>' + options.url + ' : ' + options.some;
}
test();
test({});
test({url:'myURL'});
test({some:'somethingOfMine'});
test({url:'overrideURL', some:'andSomething'});
test({url:'overrideURL', some:'andSomething', extra:'noProblem'});
EDIT: Due to comments regarding const options - the problem with using constant options in the rest of the function is actually not that you can't do that, is just that you can't use the constant variable in its own declaration - you would have to adjust the input naming to something like
function test(input_options){
const options = {
// defaults
someKey: 'someDefaultValue',
anotherKey: 'anotherDefaultValue',
// merge-in input options
...input_options
};
// from now on use options with no problem
}
Just to showcase my skills too (lol), above function can written even without having named arguments as below:
ES5 and above
function foo() {
a = typeof arguments[0] !== 'undefined' ? a : 42;
b = typeof arguments[1] !== 'undefined' ? b : 'default_b';
...
}
ES6 and above
function foo(...rest) {
a = typeof rest[0] !== 'undefined' ? a : 42;
b = typeof rest[1] !== 'undefined' ? b : 'default_b';
...
}
Yes - proof:
function read_file(file, delete_after = false) {
// Code
console.log({file,delete_after});
}
// TEST
read_file("A");
read_file("B",true);
read_file("C",false);
Yeah this is referred to as a default parameter
Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.
Syntax:
function [name]([param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]]) {
statements
}
Description:
Parameters of functions default to undefined However, in situations it might be useful to set a different default value. This is where default parameters can help.
In the past, the general strategy for setting defaults was to test parameter values in the body of the function and assign a value if they are undefined. If no value is provided in the call, its value would be undefined. You would have to set a conditional check to make sure the parameter is not undefined
With default parameters in ES2015, the check in the function body is no longer necessary. Now you can simply put a default value in the function head.
Example of the differences:
// OLD METHOD
function multiply(a, b) {
b = (typeof b !== 'undefined') ? b : 1;
return a * b;
}
multiply(5, 2); // 10
multiply(5, 1); // 5
multiply(5); // 5
// NEW METHOD
function multiply(a, b = 1) {
return a * b;
}
multiply(5, 2); // 10
multiply(5, 1); // 5
multiply(5); // 5
Different Syntax Examples:
Padding undefined vs other falsy values:
Even if the value is set explicitly when calling, the value of the num argument is the default one.
function test(num = 1) {
console.log(typeof num);
}
test(); // 'number' (num is set to 1)
test(undefined); // 'number' (num is set to 1 too)
// test with other falsy values:
test(''); // 'string' (num is set to '')
test(null); // 'object' (num is set to null)
Evaluated at call time:
The default argument gets evaluated at call time, so unlike some other languages, a new object is created each time the function is called.
function append(value, array = []) {
array.push(value);
return array;
}
append(1); //[1]
append(2); //[2], not [1, 2]
// This even applies to functions and variables
function callSomething(thing = something()) {
return thing;
}
function something() {
return 'sth';
}
callSomething(); //sth
Default parameters are available to later default parameters:
Params already encountered are available to later default parameters
function singularAutoPlural(singular, plural = singular + 's',
rallyingCry = plural + ' ATTACK!!!') {
return [singular, plural, rallyingCry];
}
//["Gecko","Geckos", "Geckos ATTACK!!!"]
singularAutoPlural('Gecko');
//["Fox","Foxes", "Foxes ATTACK!!!"]
singularAutoPlural('Fox', 'Foxes');
//["Deer", "Deer", "Deer ... change."]
singularAutoPlural('Deer', 'Deer', 'Deer peaceably and respectfully \ petition the government for positive change.')
Functions defined inside function body:
Introduced in Gecko 33 (Firefox 33 / Thunderbird 33 / SeaMonkey 2.30). Functions declared in the function body cannot be referred inside default parameters and throw a ReferenceError (currently a TypeError in SpiderMonkey, see bug 1022967). Default parameters are always executed first, function declarations inside the function body evaluate afterwards.
// Doesn't work! Throws ReferenceError.
function f(a = go()) {
function go() { return ':P'; }
}
Parameters without defaults after default parameters:
Prior to Gecko 26 (Firefox 26 / Thunderbird 26 / SeaMonkey 2.23 / Firefox OS 1.2), the following code resulted in a SyntaxError. This has been fixed in bug 777060 and works as expected in later versions. Parameters are still set left-to-right, overwriting default parameters even if there are later parameters without defaults.
function f(x = 1, y) {
return [x, y];
}
f(); // [1, undefined]
f(2); // [2, undefined]
Destructured paramet with default value assignment:
You can use default value assignment with the destructuring assignment notation
function f([x, y] = [1, 2], {z: z} = {z: 3}) {
return x + y + z;
}
f(); // 6
I've noticed a few answers mentioning that using default params isn't portable to other browsers, but it's only fair to point out that you can use transpilers like Babel to convert your code into ES5 syntax for browsers that have limited support for modern JS features.
So this:
function read_file(file, delete_after = false) {
// Code
}
would be transpiled as this (try it out in the Babel REPL -> https://babeljs.io/repl/):
"use strict";
function read_file(file) {
var delete_after =
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
//Code...
}
Of course, if you have no intention of using transpilation, then setting default params in the body of the function like others have demonstrated is perfectly fine as well.
Just a different approach to set default params is to use object map of arguments, instead of arguments directly.
For example,
const defaultConfig = {
category: 'Animals',
legs: 4
};
function checkOrganism(props) {
const category = props.category || defaultConfig.category;
const legs = props.legs || defaultConfig.legs;
}
This way, it's easy to extend the arguments and not worry about argument length mismatch.
export const getfilesize = (bytes, decimals = 2) => {
if (bytes === 0){
return '0 Bytes';
}else{
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
def read_file(file, delete_after = false)
# code
end
Following code may work in this situation including ECMAScript 6 (ES6) as well as earlier versions.
function read_file(file, delete_after) {
if(delete_after == undefined)
delete_after = false;//default value
console.log('delete_after =',delete_after);
}
read_file('text1.txt',true);
read_file('text2.txt');
as default value in languages works when the function's parameter value is skipped when calling, in JavaScript it is assigned to undefined. This approach doesn't look attractive programmatically but have backward compatibility.
The answer is yes. In fact, there are many languages who support default parameters. Python is one of them:
def(a, enter="Hello"):
print(a+enter)
Even though this is Python 3 code due to the parentheses, default parameters in functions also work in JS.
For example, and in your case:
function read_file(file, deleteAfter=false){
console.log(deleteAfter);
}
read_file("test.txt");
But sometimes you don't really need default parameters.
You can just define the variable right after the start of the function, like this:
function read_file(file){
var deleteAfter = false;
console.log(deleteAfter);
}
read_file("test.txt");
In both of my examples, it returns the same thing. But sometimes they actually could be useful, like in very advanced projects.
So, in conclusion, default parameter values can be used in JS. But it is almost the same thing as defining a variable right after the start of the function. However, sometimes they are still very useful. As you have may noticed, default parameter values take 1 less line of code than the standard way which is defining the parameter right after the start of the function.
EDIT: And this is super important! This will not work in IE. See documentation. So with IE you have to use the "define variable at top of function" method. Default parameters won't work in IE.
Yes, This will work in Javascript. You can also do that:
function func(a=10,b=20)
{
alert (a+' and '+b);
}
func(); // Result: 10 and 20
func(12); // Result: 12 and 20
func(22,25); // Result: 22 and 25

Best javascript syntactic sugar

Here are some gems:
Literals:
var obj = {}; // Object literal, equivalent to var obj = new Object();
var arr = []; // Array literal, equivalent to var arr = new Array();
var regex = /something/; // Regular expression literal, equivalent to var regex = new RegExp('something');
Defaults:
arg = arg || 'default'; // if arg evaluates to false, use 'default', which is the same as:
arg = !!arg ? arg : 'default';
Of course we know anonymous functions, but being able to treat them as literals and execute them on the spot (as a closure) is great:
(function() { ... })(); // Creates an anonymous function and executes it
Question: What other great syntactic sugar is available in javascript?
Getting the current datetime as milliseconds:
Date.now()
For example, to time the execution of a section of code:
var start = Date.now();
// some code
alert((Date.now() - start) + " ms elapsed");
Object membership test:
var props = { a: 1, b: 2 };
("a" in props) // true
("b" in props) // true
("c" in props) // false
In Mozilla (and reportedly IE7) you can create an XML constant using:
var xml = <elem></elem>;
You can substitute variables as well:
var elem = "html";
var text = "Some text";
var xml = <{elem}>{text}</{elem}>;
Using anonymous functions and a closure to create a private variable (information hiding) and the associated get/set methods:
var getter, setter;
(function()
{
var _privateVar=123;
getter = function() { return _privateVar; };
setter = function(v) { _privateVar = v; };
})()
Being able to extend native JavaScript types via prototypal inheritance.
String.prototype.isNullOrEmpty = function(input) {
return input === null || input.length === 0;
}
Use === to compare value and type:
var i = 0;
var s = "0";
if (i == s) // true
if (i === s) // false
Multi-line strings:
var str = "This is \
all one \
string.";
Since you cannot indent the subsequent lines without also adding the whitespace into the string, people generally prefer to concatenate with the plus operator. But this does provide a nice here document capability.
Resize the Length of an Array
length property is a not read only.
You can use it to increase or decrease the size of an array.
var myArray = [1,2,3];
myArray.length // 3 elements.
myArray.length = 2; //Deletes the last element.
myArray.length = 20 // Adds 18 elements to the array; the elements have the empty value. A sparse array.
Repeating a string such as "-" a specific number of times by leveraging the join method on an empty array:
var s = new Array(repeat+1).join("-");
Results in "---" when repeat == 3.
Like the default operator, || is the guard operator, &&.
answer = obj && obj.property
as opposed to
if (obj) {
answer = obj.property;
}
else {
answer = null;
}
var tags = {
name: "Jack",
location: "USA"
};
"Name: {name}<br>From {location}".replace(/\{(.*?)\}/gim, function(all, match){
return tags[match];
});
callback for string replace is just useful.
Getters and setters:
function Foo(bar)
{
this._bar = bar;
}
Foo.prototype =
{
get bar()
{
return this._bar;
},
set bar(bar)
{
this._bar = bar.toUpperCase();
}
};
Gives us:
>>> var myFoo = new Foo("bar");
>>> myFoo.bar
"BAR"
>>> myFoo.bar = "Baz";
>>> myFoo.bar
"BAZ"
This isn't a javascript exclusive, but saves like three lines of code:
check ? value1 : value2
A little bit more on levik's example:
var foo = (condition) ? value1 : value2;
The Array#forEach on Javascript 1.6
myArray.forEach(function(element) { alert(element); });
Following obj || {default:true} syntax :
calling your function with this : hello(neededOne && neededTwo && needThree) if one parameter is undefined or false then it will call hello(false), sometimes usefull
In parsing situations with a fixed set of component parts:
var str = "John Doe";
You can assign the results directly into variables, using the "destructuring assignment" synatx:
var [fname, lname] = str.split(" ");
alert(lname + ", " + fname);
Which is a bit more readable than:
var a = str.split(" ");
alert(a[1] + ", " + a[0]);
Alternately:
var [str, fname, lname] = str.match(/(.*) (.*)/);
Note that this is a Javascript 1.7 feature. So that's Mozilla 2.0+ and Chrome 6+ browsers, at this time.
Immediately Invoked Arrow function:
var test = "hello, world!";
(() => test)(); //returns "hello, world!";
I forgot:
(function() { ... }).someMethod(); // Functions as objects
Create an anonymous object literal with simply: ({})
Example: need to know if objects have the valueOf method:
var hasValueOf = !!({}).valueOf
Bonus syntactic sugar: the double-not '!!' for converting pretty much anything into a Boolean very succinctly.
I love being able to eval() a json string and get back a fully populated data structure.
I Hate having to write everything at least twice (once for IE, again for Mozilla).
Assigining the frequently used keywords (or any methods) to the simple variables like ths
var $$ = document.getElementById;
$$('samText');
JavaScript's Date class providing a semi-"Fluent Interface". This makes up for not being able to extract the date portion from a Date class directly:
var today = new Date((new Date()).setHours(0, 0, 0, 0));
It's not a fully Fluent Interface because the following will only give us a numerical value which is not actually a Date object:
var today = new Date().setHours(0, 0, 0, 0);
Default fallback:
var foo = {}; // empty object literal
alert(foo.bar) // will alert "undefined"
alert(foo.bar || "bar"); // will alert the fallback ("bar")
A practical example:
// will result in a type error
if (foo.bar.length === 0)
// with a default fallback you are always sure that the length
// property will be available.
if ((foo.bar || "").length === 0)
Here's one I just discovered: null check before calling function:
a = b && b.length;
This is a shorter equivalent to:
a = b ? b.length : null;
The best part is that you can check a property chain:
a = b && b.c && b.c.length;
I love how simple it is to work with lists:
var numberName = ["zero", "one", "two", "three", "four"][number];
And hashes:
var numberValue = {"zero":0, "one":1, "two":2, "three":3, "four":4}[numberName];
In most other languages this would be quite heavy code. Value defaults are also lovely. For example error code reporting:
var errorDesc = {301: "Moved Permanently",
404: "Resource not found",
503: "Server down"
}[errorNo] || "An unknown error has occurred";
int to string cast
var i = 12;
var s = i+"";
element.innerHTML = ""; // Replaces body of HTML element with an empty string.
A shortcut to delete all child nodes of element.
Convert string to integer defaulting to 0 if imposible,
0 | "3" //result = 3
0 | "some string" -> //result = 0
0 | "0" -> 0 //result = 0
Can be useful in some cases, mostly when 0 is considered as bad result
Template literals
var a = 10;
var b = 20;
var text = `${a} + ${b} = ${a+b}`;
then the text variable will be like below!
10 + 20 = 30

Hidden Features of JavaScript? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
What "Hidden Features" of JavaScript do you think every programmer should know?
After having seen the excellent quality of the answers to the following questions I thought it was time to ask it for JavaScript.
Hidden Features of HTML
Hidden Features of CSS
Hidden Features of PHP
Hidden Features of ASP.NET
Hidden Features of C#
Hidden Features of Java
Hidden Features of Python
Even though JavaScript is arguably the most important Client Side language right now (just ask Google) it's surprising how little most web developers appreciate how powerful it really is.
You don't need to define any parameters for a function. You can just use the function's arguments array-like object.
function sum() {
var retval = 0;
for (var i = 0, len = arguments.length; i < len; ++i) {
retval += arguments[i];
}
return retval;
}
sum(1, 2, 3) // returns 6
I could quote most of Douglas Crockford's excellent book
JavaScript: The Good Parts.
But I'll take just one for you, always use === and !== instead of == and !=
alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true
== is not transitive. If you use === it would give false for
all of these statements as expected.
Functions are first class citizens in JavaScript:
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
var sum = function(x,y,z) {
return x+y+z;
};
alert( passFunAndApply(sum,3,4,5) ); // 12
Functional programming techniques can be used to write elegant javascript.
Particularly, functions can be passed as parameters, e.g. Array.filter() accepts a callback:
[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]
You can also declare a "private" function that only exists within the scope of a specific function:
function PrintName() {
var privateFunction = function() { return "Steve"; };
return privateFunction();
}
You can use the in operator to check if a key exists in an object:
var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true
If you find the object literals too ugly you can combine it with the parameterless function tip:
function list()
{ var x = {};
for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
return x
}
5 in list(1,2,3,4,5) //true
Assigning default values to variables
You can use the logical or operator || in an assignment expression to provide a default value:
var a = b || c;
The a variable will get the value of c only if b is falsy (if is null, false, undefined, 0, empty string, or NaN), otherwise a will get the value of b.
This is often useful in functions, when you want to give a default value to an argument in case isn't supplied:
function example(arg1) {
arg1 || (arg1 = 'default value');
}
Example IE fallback in event handlers:
function onClick(e) {
e || (e = window.event);
}
The following language features have been with us for a long time, all JavaScript implementations support them, but they weren't part of the specification until ECMAScript 5th Edition:
The debugger statement
Described in: § 12.15 The debugger statement
This statement allows you to put breakpoints programmatically in your code just by:
// ...
debugger;
// ...
If a debugger is present or active, it will cause it to break immediately, right on that line.
Otherwise, if the debugger is not present or active this statement has no observable effect.
Multiline String literals
Described in: § 7.8.4 String Literals
var str = "This is a \
really, really \
long line!";
You have to be careful because the character next to the \ must be a line terminator, if you have a space after the \ for example, the code will look exactly the same, but it will raise a SyntaxError.
JavaScript does not have block scope (but it has closure so let's call it even?).
var x = 1;
{
var x = 2;
}
alert(x); // outputs 2
You can access object properties with [] instead of .
This allows you look up a property matching a variable.
obj = {a:"test"};
var propname = "a";
var b = obj[propname]; // "test"
You can also use this to get/set object properties whose name is not a legal identifier.
obj["class"] = "test"; // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.
Some people don't know this and end up using eval() like this, which is a really bad idea:
var propname = "a";
var a = eval("obj." + propname);
This is harder to read, harder to find errors in (can't use jslint), slower to execute, and can lead to XSS exploits.
If you're Googling for a decent JavaScript reference on a given topic, include the "mdc" keyword in your query and your first results will be from the Mozilla Developer Center. I don't carry any offline references or books with me. I always use the "mdc" keyword trick to directly get to what I'm looking for. For example:
Google: javascript array sort mdc
(in most cases you may omit "javascript")
Update: Mozilla Developer Center has been renamed to Mozilla Developer Network. The "mdc" keyword trick still works, but soon enough we may have to start using "mdn" instead.
Maybe a little obvious to some...
Install Firebug and use console.log("hello"). So much better than using random alert();'s which I remember doing a lot a few years ago.
Private Methods
An object can have private methods.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
// A private method only visible from within this constructor
function calcFullName() {
return firstName + " " + lastName;
}
// A public method available to everyone
this.sayHello = function () {
alert(calcFullName());
}
}
//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();
// This fails since the method is not visible from this scope
alert(person1.calcFullName());
Also mentioned in Crockford's "Javascript: The Good Parts":
parseInt() is dangerous. If you pass it a string without informing it of the proper base it may return unexpected numbers. For example parseInt('010') returns 8, not 10. Passing a base to parseInt makes it work correctly:
parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
Functions are objects and therefore can have properties.
fn = function(x) {
// ...
}
fn.foo = 1;
fn.next = function(y) {
//
}
I'd have to say self-executing functions.
(function() { alert("hi there");})();
Because Javascript doesn't have block scope, you can use a self-executing function if you want to define local variables:
(function() {
var myvar = 2;
alert(myvar);
})();
Here, myvar is does not interfere with or pollute the global scope, and disappears when the function terminates.
Know how many parameters are expected by a function
function add_nums(num1, num2, num3 ){
return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.
Know how many parameters are received by the function
function add_many_nums(){
return arguments.length;
}
add_many_nums(2,1,122,12,21,89); //returns 6
Here are some interesting things:
Comparing NaN with anything (even NaN) is always false, that includes ==, < and >.
NaN Stands for Not a Number but if you ask for the type it actually returns a number.
Array.sort can take a comparator function and is called by a quicksort-like driver (depends on implementation).
Regular expression "constants" can maintain state, like the last thing they matched.
Some versions of JavaScript allow you to access $0, $1, $2 members on a regex.
null is unlike anything else. It is neither an object, a boolean, a number, a string, nor undefined. It's a bit like an "alternate" undefined. (Note: typeof null == "object")
In the outermost context, this yields the otherwise unnameable [Global] object.
Declaring a variable with var, instead of just relying on automatic declaration of the variable gives the runtime a real chance of optimizing access to that variable
The with construct will destroy such optimzations
Variable names can contain Unicode characters.
JavaScript regular expressions are not actually regular. They are based on Perl's regexs, and it is possible to construct expressions with lookaheads that take a very, very long time to evaluate.
Blocks can be labeled and used as the targets of break. Loops can be labeled and used as the target of continue.
Arrays are not sparse. Setting the 1000th element of an otherwise empty array should fill it with undefined. (depends on implementation)
if (new Boolean(false)) {...} will execute the {...} block
Javascript's regular expression engine's are implementation specific: e.g. it is possible to write "non-portable" regular expressions.
[updated a little in response to good comments; please see comments]
I know I'm late to the party, but I just can't believe the + operator's usefulness hasn't been mentioned beyond "convert anything to a number". Maybe that's how well hidden a feature it is?
// Quick hex to dec conversion:
+"0xFF"; // -> 255
// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();
// Safer parsing than parseFloat()/parseInt()
parseInt("1,000"); // -> 1, not 1000
+"1,000"; // -> NaN, much better for testing user input
parseInt("010"); // -> 8, because of the octal literal prefix
+"010"; // -> 10, `Number()` doesn't parse octal literals
// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null; // -> 0;
// Boolean to integer
+true; // -> 1;
+false; // -> 0;
// Other useful tidbits:
+"1e10"; // -> 10000000000
+"1e-4"; // -> 0.0001
+"-12"; // -> -12
Of course, you can do all this using Number() instead, but the + operator is so much prettier!
You can also define a numeric return value for an object by overriding the prototype's valueOf() method. Any number conversion performed on that object will not result in NaN, but the return value of the valueOf() method:
var rnd = {
"valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd; // -> 442;
+rnd; // -> 727;
+rnd; // -> 718;
"Extension methods in JavaScript" via the prototype property.
Array.prototype.contains = function(value) {
for (var i = 0; i < this.length; i++) {
if (this[i] == value) return true;
}
return false;
}
This will add a contains method to all Array objects. You can call this method using this syntax
var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");
To properly remove a property from an object, you should delete the property instead of just setting it to undefined:
var obj = { prop1: 42, prop2: 43 };
obj.prop2 = undefined;
for (var key in obj) {
...
The property prop2 will still be part of the iteration. If you want to completely get rid of prop2, you should instead do:
delete obj.prop2;
The property prop2 will no longer will make an appearance when you're iterating through the properties.
with.
It's rarely used, and frankly, rarely useful... But, in limited circumstances, it does have its uses.
For instance: object literals are quite handy for quickly setting up properties on a new object. But what if you need to change half of the properties on an existing object?
var user =
{
fname: 'Rocket',
mname: 'Aloysus',
lname: 'Squirrel',
city: 'Fresno',
state: 'California'
};
// ...
with (user)
{
mname = 'J';
city = 'Frostbite Falls';
state = 'Minnesota';
}
Alan Storm points out that this can be somewhat dangerous: if the object used as context doesn't have one of the properties being assigned to, it will be resolved in the outer scope, possibly creating or overwriting a global variable. This is especially dangerous if you're used to writing code to work with objects where properties with default or empty values are left undefined:
var user =
{
fname: "John",
// mname definition skipped - no middle name
lname: "Doe"
};
with (user)
{
mname = "Q"; // creates / modifies global variable "mname"
}
Therefore, it is probably a good idea to avoid the use of the with statement for such assignment.
See also: Are there legitimate uses for JavaScript’s “with” statement?
Methods (or functions) can be called on object that are not of the type they were designed to work with. This is great to call native (fast) methods on custom objects.
var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });
This code crashes because listNodes is not an Array
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
This code works because listNodes defines enough array-like properties (length, [] operator) to be used by sort().
Prototypal inheritance (popularized by Douglas Crockford) completely revolutionizes the way you think about loads of things in Javascript.
Object.beget = (function(Function){
return function(Object){
Function.prototype = Object;
return new Function;
}
})(function(){});
It's a killer! Pity how almost no one uses it.
It allows you to "beget" new instances of any object, extend them, while maintaining a (live) prototypical inheritance link to their other properties. Example:
var A = {
foo : 'greetings'
};
var B = Object.beget(A);
alert(B.foo); // 'greetings'
// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo); // 'hello'
A.bar = 'world';
alert(B.bar); // 'world'
// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo); // 'hello'
B.bar = 'universe';
alert(A.bar); // 'world'
Some would call this a matter of taste, but:
aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }
The trinary operator can be chained to act like Scheme's (cond ...):
(cond (predicate (action ...))
(predicate2 (action2 ...))
(#t default ))
can be written as...
predicate ? action( ... ) :
predicate2 ? action2( ... ) :
default;
This is very "functional", as it branches your code without side effects. So instead of:
if (predicate) {
foo = "one";
} else if (predicate2) {
foo = "two";
} else {
foo = "default";
}
You can write:
foo = predicate ? "one" :
predicate2 ? "two" :
"default";
Works nice with recursion, too :)
Numbers are also objects. So you can do cool stuff like:
// convert to base 2
(5).toString(2) // returns "101"
// provide built in iteration
Number.prototype.times = function(funct){
if(typeof funct === 'function') {
for(var i = 0;i < Math.floor(this);i++) {
funct(i);
}
}
return this;
}
(5).times(function(i){
string += i+" ";
});
// string now equals "0 1 2 3 4 "
var x = 1000;
x.times(function(i){
document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document
How about closures in JavaScript (similar to anonymous methods in C# v2.0+). You can create a function that creates a function or "expression".
Example of closures:
//Takes a function that filters numbers and calls the function on
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
var filteredNumbers = [];
for (var index = 0; index < numbers.length; index++)
{
if (filterFunction(numbers[index]) == true)
{
filteredNumbers.push(numbers[index]);
}
}
return filteredNumbers;
}
//Creates a function (closure) that will remember the value "lowerBound"
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}
var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];
var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);
numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);
numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
You can also extend (inherit) classes and override properties/methods using the prototype chain spoon16 alluded to.
In the following example we create a class Pet and define some properties. We also override the .toString() method inherited from Object.
After this we create a Dog class which extends Pet and overrides the .toString() method again changing it's behavior (polymorphism). In addition we add some other properties to the child class.
After this we check the inheritance chain to show off that Dog is still of type Dog, of type Pet, and of type Object.
// Defines a Pet class constructor
function Pet(name)
{
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
}
// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function()
{
return 'This pets name is: ' + this.getName();
};
// end of class Pet
// Define Dog class constructor (Dog : Pet)
function Dog(name, breed)
{
// think Dog : base(name)
Pet.call(this, name);
this.getBreed = function() { return breed; };
}
// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();
// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;
// Now we override Pet.prototype.toString
Dog.prototype.toString = function()
{
return 'This dogs name is: ' + this.getName() +
', and its breed is: ' + this.getBreed();
};
// end of class Dog
var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);
// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true
Both answers to this question were codes modified from a great MSDN article by Ray Djajadinata.
You may catch exceptions depending on their type. Quoted from MDC:
try {
myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
// statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
// statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
// statements to handle EvalError exceptions
} catch (e) {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
NOTE: Conditional catch clauses are a Netscape (and hence Mozilla/Firefox) extension that is not part of the ECMAScript specification and hence cannot be relied upon except on particular browsers.
Off the top of my head...
Functions
arguments.callee refers to the function that hosts the "arguments" variable, so it can be used to recurse anonymous functions:
var recurse = function() {
if (condition) arguments.callee(); //calls recurse() again
}
That's useful if you want to do something like this:
//do something to all array items within an array recursively
myArray.forEach(function(item) {
if (item instanceof Array) item.forEach(arguments.callee)
else {/*...*/}
})
Objects
An interesting thing about object members: they can have any string as their names:
//these are normal object members
var obj = {
a : function() {},
b : function() {}
}
//but we can do this too
var rules = {
".layout .widget" : function(element) {},
"a[href]" : function(element) {}
}
/*
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
var elements = document.querySelectorAll(rules[item]);
for (var e, i = 0; e = elements[i++];) rules[item](e);
}
Strings
String.split can take regular expressions as parameters:
"hello world with spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]
String.replace can take a regular expression as a search parameter and a function as a replacement parameter:
var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"
You can use objects instead of switches most of the time.
function getInnerText(o){
return o === null? null : {
string: o,
array: o.map(getInnerText).join(""),
object:getInnerText(o["childNodes"])
}[typeis(o)];
}
Update: if you're concerned about the cases evaluating in advance being inefficient (why are you worried about efficiency this early on in the design of the program??) then you can do something like this:
function getInnerText(o){
return o === null? null : {
string: function() { return o;},
array: function() { return o.map(getInnerText).join(""); },
object: function () { return getInnerText(o["childNodes"]; ) }
}[typeis(o)]();
}
This is more onerous to type (or read) than either a switch or an object, but it preserves the benefits of using an object instead of a switch, detailed in the comments section below. This style also makes it more straightforward to spin this out into a proper "class" once it grows up enough.
update2: with proposed syntax extensions for ES.next, this becomes
let getInnerText = o -> ({
string: o -> o,
array: o -> o.map(getInnerText).join(""),
object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
Be sure to use the hasOwnProperty method when iterating through an object's properties:
for (p in anObject) {
if (anObject.hasOwnProperty(p)) {
//Do stuff with p here
}
}
This is done so that you will only access the direct properties of anObject, and not use the properties that are down the prototype chain.
Private variables with a Public Interface
It uses a neat little trick with a self-calling function definition.
Everything inside the object which is returned is available in the public interface, while everything else is private.
var test = function () {
//private members
var x = 1;
var y = function () {
return x * 2;
};
//public interface
return {
setx : function (newx) {
x = newx;
},
gety : function () {
return y();
}
}
}();
assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());

Categories

Resources