There are several JavaScript tricks that are used widely by experienced programmers. Many of them may not be instantly obvious, especially for beginners. These tricks use language features not by their direct purpose, but rather by their side-effect to achieve goals, that can’t be achieved by default language means. Here I made a little compilation of such tricks with explanation.
You should understand that most of these tricks are rather hacks and not something you should use in your daily development. The purpose of that article is to explain how they work, not to push to use them.
Using !! to convert value into a boolean
Everything in JavaScript can be interpreted either as truthy or falsy. This means that when you put object into an if expression, it will either let you go by true-branch (i.e. it’s ‘truthy’) of by false branch(‘falsy’).
0, false, "", null, undefined, NaN are all falsy, every other object is truthy. Sometimes you’d like to convert an object to a plain boolean value. To do this you can use double negation !!.
Another side of that coin is that instead of if (x == "test") you can simply write if (x). In case x is empty (hence falsy) it will run else block.
Converting string into a number with +str
In JavaScript + is a unary operator that returns a numeric representation of operand or NaN if not applicable. Sometimes you can enforce code (see underscorejs sources) such as x === +x, which is thus just checking whether x is numeric.
This one is not obvious at all. Normally you’d want to use parseFloat and parseInt(x, 10) for parsing numbers.
Providing default value with ||
In JavaScript || is an example of short-circuit evaluation, which is also commonly used in some other language. This operator will firstly evaluate expression on the left side, and then, if falsy, will proceed with on the right. In any case it will return first non-falsy result. Consider the following example
function setAge(age) {
this.age = age || 10
}
setAge();
We didn’t provide age, thus age || 10 will return 10, which is a nice way to provide some defaults values. In fact this is equivalent to
var x;
if (age) {
this.age = age;
} else {
this.age = 10;
}
The former is obviously more succinct and that’s why you will see it used everywhere.
Personally, I use that pattern a lot. I like its conciseness and clarity. Notice, however, that since 0 is falsy you won’t have an ability to set age to 0. Therefore, it might be a better (but slightly more verbose) solution to use something like this:
this.age = (typeof age !== "undefined") ? age : 10;
Using void 0 instead of undefined
Keyword void takes one argument and always return undefined. Why not to simply use undefined? Because in some browsers undefined is just a variable that can be reassigned, and the former gives us a higher level of confidence. Although you can find this being used in source code of some libraries, I would not recommend using it on the regular basis, since all EC5-compliant browsers don’t allow to rewrite undefined.
Encapsulation with (function() {...})() pattern
You’ll want to wrap your code into an anonymous function and then immediately call it, when you want some encapsulation. There are only two types of scopes in JavaScript (but look at ECMA6 block scopes): global scope and function scope. Everything you write goes into global scope, which is accessible from everywhere. This includes vars and function declarations. Normally you’d want to encapsulate most of the code somewhere inside of a function and expose to global scope only interface. And that’s where this pattern gets really handy. Consider the following:
(function() {
function div(a, b) {
return a / b;
}
function divBy5(x) {
return div(x, 5);
}
window.divBy5 = divBy5;
})()
div // => undefined
divBy5(10); // => 2
Among other hacks listed in this article this one is really harmless and you can and should use it in your code to prevent exposing some inner logic into the global scope.
In conclusion, I’d like to remind you that any code you write should be simple and clear to other programmers. And any natural constructions provided by language should be preferred to artificial ones.
Some of the problems, listed in that article are being solved in elegant way by ES6 standart (next version of JavaScript). For instance, you’ll probably won’t need the obscure age = age || 10 pattern in the future, since ES6 allows you to write default arguments in a better way:
function(age = 10) {
...
}
Another example, is (function() {...})() pattern, which you can leave in the past after EC6 modules get implemented by modern browsers.
Append an array to another array
var a = [4,5,6];
var b = [7,8,9];
Array.prototype.push.apply(a, b);
uneval(a); // is: [4, 5, 6, 7, 8, 9]
Milliseconds since epoch
+new Date() // 1259359833574
Simulate threads using yield operator
JavaScript 1.7
//// thread definition
function Thread( name ) {
for ( var i = 0; i < 5; i++ ) {
Print(name+': '+i);
yield;
}
}
//// thread management
var threads = [];
// thread creation
threads.push( new Thread('foo') );
threads.push( new Thread('bar') );
// scheduler
while (threads.length) {
var thread = threads.shift();
try {
thread.next();
threads.push(thread);
} catch(ex if ex instanceof StopIteration) {}
}
prints:
foo: 0
bar: 0
foo: 1
bar: 1
foo: 2
bar: 2
foo: 3
bar: 3
foo: 4
bar: 4
prefix an integer with zeros
function PrefixInteger(num, length) {
return (num / Math.pow(10, length)).toFixed(length).substr(2);
}
And more efficient:
function PrefixInteger(num, length) {
return ( "0000000000000000" + num ).substr( -length );
}
Thanks to fritzthe...
And even better:
function PrefixInteger(num, length) {
return (Array(length).join('0') + num).slice(-length);
}
Thanks to tobeytai...
shuffle the Array
JavaScript 1.?
var list = [1,2,3,4,5,6,7,8,9];
list = list.sort(function() Math.random() - 0.5);
Print(list); // prints something like: 4,3,1,2,9,5,6,7,8
multi-line text
JavaScript 1.6
var text = <>
this
is
my
multi-line
text
</>.toString();
Print(text);
prints:
this
is
my
multi-line
text
note
If you want to support special XML chars, you can use a CDATA section:
<><![CDATA[
>>> hello
]]></>.toString();
Escape and unescape HTML entities
const entityToCode = { __proto__: null,
apos:0x0027,quot:0x0022,amp:0x0026,lt:0x003C,gt:0x003E,nbsp:0x00A0,iexcl:0x00A1,cent:0x00A2,pound:0x00A3,
curren:0x00A4,yen:0x00A5,brvbar:0x00A6,sect:0x00A7,uml:0x00A8,copy:0x00A9,ordf:0x00AA,laquo:0x00AB,
not:0x00AC,shy:0x00AD,reg:0x00AE,macr:0x00AF,deg:0x00B0,plusmn:0x00B1,sup2:0x00B2,sup3:0x00B3,
acute:0x00B4,micro:0x00B5,para:0x00B6,middot:0x00B7,cedil:0x00B8,sup1:0x00B9,ordm:0x00BA,raquo:0x00BB,
frac14:0x00BC,frac12:0x00BD,frac34:0x00BE,iquest:0x00BF,Agrave:0x00C0,Aacute:0x00C1,Acirc:0x00C2,Atilde:0x00C3,
Auml:0x00C4,Aring:0x00C5,AElig:0x00C6,Ccedil:0x00C7,Egrave:0x00C8,Eacute:0x00C9,Ecirc:0x00CA,Euml:0x00CB,
Igrave:0x00CC,Iacute:0x00CD,Icirc:0x00CE,Iuml:0x00CF,ETH:0x00D0,Ntilde:0x00D1,Ograve:0x00D2,Oacute:0x00D3,
Ocirc:0x00D4,Otilde:0x00D5,Ouml:0x00D6,times:0x00D7,Oslash:0x00D8,Ugrave:0x00D9,Uacute:0x00DA,Ucirc:0x00DB,
Uuml:0x00DC,Yacute:0x00DD,THORN:0x00DE,szlig:0x00DF,agrave:0x00E0,aacute:0x00E1,acirc:0x00E2,atilde:0x00E3,
auml:0x00E4,aring:0x00E5,aelig:0x00E6,ccedil:0x00E7,egrave:0x00E8,eacute:0x00E9,ecirc:0x00EA,euml:0x00EB,
igrave:0x00EC,iacute:0x00ED,icirc:0x00EE,iuml:0x00EF,eth:0x00F0,ntilde:0x00F1,ograve:0x00F2,oacute:0x00F3,
ocirc:0x00F4,otilde:0x00F5,ouml:0x00F6,divide:0x00F7,oslash:0x00F8,ugrave:0x00F9,uacute:0x00FA,ucirc:0x00FB,
uuml:0x00FC,yacute:0x00FD,thorn:0x00FE,yuml:0x00FF,OElig:0x0152,oelig:0x0153,Scaron:0x0160,scaron:0x0161,
Yuml:0x0178,fnof:0x0192,circ:0x02C6,tilde:0x02DC,Alpha:0x0391,Beta:0x0392,Gamma:0x0393,Delta:0x0394,
Epsilon:0x0395,Zeta:0x0396,Eta:0x0397,Theta:0x0398,Iota:0x0399,Kappa:0x039A,Lambda:0x039B,Mu:0x039C,
Nu:0x039D,Xi:0x039E,Omicron:0x039F,Pi:0x03A0,Rho:0x03A1,Sigma:0x03A3,Tau:0x03A4,Upsilon:0x03A5,
Phi:0x03A6,Chi:0x03A7,Psi:0x03A8,Omega:0x03A9,alpha:0x03B1,beta:0x03B2,gamma:0x03B3,delta:0x03B4,
epsilon:0x03B5,zeta:0x03B6,eta:0x03B7,theta:0x03B8,iota:0x03B9,kappa:0x03BA,lambda:0x03BB,mu:0x03BC,
nu:0x03BD,xi:0x03BE,omicron:0x03BF,pi:0x03C0,rho:0x03C1,sigmaf:0x03C2,sigma:0x03C3,tau:0x03C4,
upsilon:0x03C5,phi:0x03C6,chi:0x03C7,psi:0x03C8,omega:0x03C9,thetasym:0x03D1,upsih:0x03D2,piv:0x03D6,
ensp:0x2002,emsp:0x2003,thinsp:0x2009,zwnj:0x200C,zwj:0x200D,lrm:0x200E,rlm:0x200F,ndash:0x2013,
mdash:0x2014,lsquo:0x2018,rsquo:0x2019,sbquo:0x201A,ldquo:0x201C,rdquo:0x201D,bdquo:0x201E,dagger:0x2020,
Dagger:0x2021,bull:0x2022,hellip:0x2026,permil:0x2030,prime:0x2032,Prime:0x2033,lsaquo:0x2039,rsaquo:0x203A,
oline:0x203E,frasl:0x2044,euro:0x20AC,image:0x2111,weierp:0x2118,real:0x211C,trade:0x2122,alefsym:0x2135,
larr:0x2190,uarr:0x2191,rarr:0x2192,darr:0x2193,harr:0x2194,crarr:0x21B5,lArr:0x21D0,uArr:0x21D1,
rArr:0x21D2,dArr:0x21D3,hArr:0x21D4,forall:0x2200,part:0x2202,exist:0x2203,empty:0x2205,nabla:0x2207,
isin:0x2208,notin:0x2209,ni:0x220B,prod:0x220F,sum:0x2211,minus:0x2212,lowast:0x2217,radic:0x221A,
prop:0x221D,infin:0x221E,ang:0x2220,and:0x2227,or:0x2228,cap:0x2229,cup:0x222A,int:0x222B,
there4:0x2234,sim:0x223C,cong:0x2245,asymp:0x2248,ne:0x2260,equiv:0x2261,le:0x2264,ge:0x2265,
sub:0x2282,sup:0x2283,nsub:0x2284,sube:0x2286,supe:0x2287,oplus:0x2295,otimes:0x2297,perp:0x22A5,
sdot:0x22C5,lceil:0x2308,rceil:0x2309,lfloor:0x230A,rfloor:0x230B,lang:0x2329,rang:0x232A,loz:0x25CA,
spades:0x2660,clubs:0x2663,hearts:0x2665,diams:0x2666
};
var charToEntity = {};
for ( var entityName in entityToCode )
charToEntity[String.fromCharCode(entityToCode[entityName])] = entityName;
function UnescapeEntities(str) str.replace(/&(.+?);/g, function(str, ent) String.fromCharCode( ent[0]!='#' ? entityToCode[ent] : ent[1]=='x' ? parseInt(ent.substr(2),16): parseInt(ent.substr(1)) ) );
function EscapeEntities(str) str.replace(/[^\x20-\x7E]/g, function(str) charToEntity[str] ? '&'+charToEntity[str]+';' : str );
Remove an object from an array
JavaScript 1.8
function RemoveArrayElement( array, element ) !!let (pos=array.lastIndexOf(element)) pos != -1 && array.splice(pos, 1);
Creates a random alphabetic string
function RandomString(length) {
var str = '';
for ( ; str.length < length; str += Math.random().toString(36).substr(2) );
return str.substr(0, length);
}
Brainfuck interpreter
var code = '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.';
var inp = '23\n';
var out = '';
var codeSize = code.length;
var i = 0, ip = 0, cp = 0, dp = 0, m = {};
var loopIn = {}, loopOut = {};
var tmp = [];
for ( var cp = 0; cp < codeSize ; cp++ )
if ( code[cp] == '[' )
tmp.push(cp);
else
if ( code[cp] == ']' )
loopOut[loopIn[cp] = tmp.pop()] = cp;
for (var cp = 0; cp < codeSize && i < 100000; cp++, i++) {
switch(code[cp]) {
case '>': dp++; break;
case '<': dp--; break;
case '+': m[dp] = ((m[dp]||0)+1)&255; break
case '-': m[dp] = ((m[dp]||0)-1)&255; break;
case '.': out += String.fromCharCode(m[dp]); break;
case ',': m[dp] = inp.charCodeAt(ip++)||0; break;
case '[': m[dp]||(cp=loopOut[cp]); break;
case ']': cp = loopIn[cp]-1; break;
}
}
Print(out);
This Brainfuck program just prints 'Hello World!'
Optional named function arguments
function foo({ name:name, project:project}) {
Print( project );
Print( name );
}
foo({ name:'soubok', project:'jslibs' })
foo({ project:'jslibs', name:'soubok'})
String converter
JavaScript 1.8
function CreateTranslator(translationTable) function(s) s.replace(new RegExp([k for (k in translationTable)].join('|'), 'g'), function(str) translationTable[str]);
exemple of use:
var translationTable = { a:1, bb:2, b:3, c:4 };
var MyTranslater = CreateTranslator( translationTable );
MyTranslater('aabbbc'); // returns: 11234
Display the current call stack
function Stack() { try { throw Error() } catch(ex) { return ex.stack } }
print( Stack() );
prints:
Error()@:0
Stack()@test.js:1
@test.js:3
Change the primitive value of an object with valueOf
function foo() {
this.valueOf = function() {
return 'this is my value';
}
}
var bar = new foo();
Print( bar ); // prints: this is my value
Print( bar == 'this is my value' ) // prints: true
Print( bar === 'this is my value' ) // prints: false
Transform the arguments object into an array
JavaScript 1.6
function foo() {
var argArray = Array.slice(arguments); // is ['aa',11]
}
foo('aa',11);
use also
var argArray = Array.prototype.slice.call(arguments);
Convert a string into a charcode list
Method 1: JavaScript 1.6
Array.map('foo', function(x) { return String.charCodeAt(x) }) // is [112,111,111]
Method 2: JavaScript 1.7
[ String.charCodeAt(x) for each ( x in 'foo' ) ] // is [112,111,111]
Array iteration pitfall
var foo = [3,4,5];
for ( var i in foo ) {
if ( i == 1 ) {
foo.unshift(6);
}
Print('item: '+foo[i])
}
Print( 'array: '+foo.toSource() )
output:
item: 3
item: 3
item: 4
array: [6, 3, 4, 5]
exceptions for non-fatal errors
JavaScript 1.7
function ERR() { throw ERR }
function CHK( v ) { return v || ERR() }
try {
var data1 = 'a/b/c';
var arr1 = CHK(data1).split('/');
var data2 = '';
var arr2 = CHK(data2).split('/'); // the exception is throw here
} catch(ex if ex == ERR) {
Print('non fatal error while decoding the data')
}
prints:
a b c
A Switch function
JavaScript 1.8
function Switch(i) arguments[++i];
usage:
Print( Switch(2,'aa','bb','cc','dd') ); // prints: cc
Print( Switch(100,'aa','bb','cc','dd') ); // prints: undefined
A Match function
JavaScript 1.8
function Match(v) Array.indexOf(arguments,v,1)-1;
usage:
Print( Match('aa', '0', 'b', 123, 'aa', 8.999 ) ); // prints: 3
Print( Match('Z', 'b', 123, 'aa', 8.999 ) ); // prints: -2 (< 0 is not found)
new object override
JavaScript 1.5
function foo() {
return new Array(5,6,7);
}
var bar = new foo();
bar.length // is 3
Kind of destructuring assignments
JavaScript 1.7
var { a:x, b:y } = { a:7, b:8 };
Print(x); // prints: 7
Print(y); // prints: 8
Generator Expressions
JavaScript 1.7
[ y for ( y in [5,6,7,8,9] ) ] // is [0,1,2,3,4]
and
[ y for each ( y in [5,6,7,8,9] ) ] // is [5,6,7,8,9]
Because in for extracts index names, and for each extracts the values.
Advanced use of iterators
JavaScript 1.7
Number.prototype.__iterator__ = function() {
for ( let i = 0; i < this; i++ )
yield i;
};
for ( let i in 5 )
print(i);
prints:
1
2
3
4
5
This make Number object to act as a generator.
Expression Closures
JavaScript 1.8
function(x) x * x;
Note that braces {...} and return are implicit
Function declaration and expression
Function declaration:
bar(); // prints: bar
function bar() {
Print('bar');
}
function foo() {
Print('foo');
}
foo(); // prints: foo
Function expression:
(function foo() {
Print( foo.name );
})(); // prints: foo
foo(); // rise a ReferenceError
!function foo() {
}
foo(); // rise a ReferenceError
var bar = function foo() {
}
foo(); // rise a ReferenceError
Factory method pattern
Complex = new function() {
function Complex(a, b) {
// ...
}
this.fromCartesian = function(real, mag) {
return new Complex(real, imag);
}
this.fromPolar = function(rho, theta) {
return new Complex(rho * Math.cos(theta), rho * Math.sin(theta));
}
}
var c = Complex.fromPolar(1, Math.pi); // Same as fromCartesian(-1, 0);
see factory pattern on wikipedia
Closures by example
JavaScript 1.5
function CreateAdder( add ) {
return function( value ) {
return value + add;
}
}
usage:
var myAdder5 = CreateAdder( 5 );
var myAdder6 = CreateAdder( 6 );
Print( myAdder5( 2 ) ); // prints 7
Print( myAdder6( 4 ) ); // prints 10
Further information about Nested functions and closures
SyntaxError
JavaScript 1.5
raised when a syntax error occurs while parsing code in eval()
try {
eval('1 + * 5'); // will rise a SyntaxError exception
} catch( ex ) {
Print( ex.constructor == SyntaxError ); // Prints true
}
JavaScript 1.7
try {
eval('1 + * 5');
} catch( ex if ex instanceof SyntaxError ) {
Print( 'SyntaxError !' ); // prints: SyntaxError !
}
ReferenceError
raised when de-referencing an invalid reference.
try {
fooBar(); // will rise a ReferenceError exception
} catch( ex ) {
Print( ex.constructor == ReferenceError ); // Prints true
}
JavaScript 1.7
try {
fooBar();
} catch( ex if ex instanceof ReferenceError ) {
Print( 'ReferenceError !' ); // prints: ReferenceError !
}
JavaScript Minifier / comment remover
jslibs
var script = new Script('var a; /* this is a variable */ var b; // another variable');
Print( script.toString() );
prints:
var a;
var b;
javascript
function foo() {
var a; // a variable
var b = [1, 2, 3]; // an array
var c = {x: {y: 1}};
function bar() { // my function
return 1 /* 2 */;
}
}
Print( foo.toSource() );
JavaScript code beautifier (un-minifier)
function foo() {var a;var b=[1,2,3];var c={x:{y:1}};function bar(){return 1}}
Print( foo.toSource(1) );
prints:
function foo() {
var a;
var b = [1, 2, 3];
var c = {x: {y: 1}};
function bar() {
return 1;
}
}
Auto indent JavaScript code / unobfuscator
Spidermonkey JavaScript engine only
function foo() { function bar(){};var i=0;for(;i<10;++i) bar(i) }
Print(foo.toSource(2));
prints:
function foo() {
function bar() {
}
var i = 0;
for (; i < 10; ++i) {
bar(i);
}
}
Objects, constructor and instanceof
JavaScript 1.5
function Foo() {
// Foo class
}
var a = new Foo();
Bar.prototype = new Foo();
function Bar() {
// Bar class
}
var b = new Bar();
Print( a.constructor == Foo ); // true
Print( a instanceof Foo ); // true
Print( b.constructor == Foo ); // true
Print( b instanceof Foo ); // true
Print( b.constructor == Bar ); // false !
Print( b instanceof Bar ); // true
Objects private and public members
function MyConstructor( pub, priv ) {
var privateVariable = priv;
this.publicVariable = pub;
this.SetThePrivateVariable = function( value ) {
privateVariable = value;
}
this.GetThePrivateVariable = function() {
return privateVariable;
}
this.PrintAllVariables = function() {
Print( privateVariable + ',' + this.publicVariable );
}
}
var myObject = new MyConstructor( 123, 456 );
Print( myObject.privateVariable ); // prints: undefined
Print( myObject.publicVariable ); // prints: 123
myObject.PrintAllVariables(); // prints 456,123
myObject.SetThePrivateVariable( 789 );
myObject.PrintAllVariables(); // prints 789,123
Stack data structure
JavaScript 1.5 Array object has all needed methods to be used as a stack.
var stack = [];
stack.push( 111 );
stack.push( 2.22 );
stack.push( 'ccc' );
Print( stack ); // prints: 111,2.22,ccc
Print( stack.pop() ); // prints: ccc
Print( stack.pop() ); // prints: 2.22
Singleton pattern
JavaScript 1.5 The singleton pattern is a design pattern that is used to restrict instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
function MySingletonClass() {
if ( arguments.callee._singletonInstance )
return arguments.callee._singletonInstance;
arguments.callee._singletonInstance = this;
this.Foo = function() {
// ...
}
}
var a = new MySingletonClass()
var b = MySingletonClass()
Print( a === b ); // prints: true
Use Functions as an Object
JavaScript 1.5
function Counter() {
if ( !arguments.callee.count ) {
arguments.callee.count = 0;
}
return arguments.callee.count++;
}
Print( Counter() ); // prints: 0
Print( Counter() ); // prints: 1
Print( Counter() ); // prints: 2
E4X add nodes
JavaScript 1.6
var x = <store/>;
x.* += <item price="10" />;
x.* += <item price="15" />;
Print( x.toXMLString() );
prints:
<store>
<item price="10"/>
<item price="15"/>
</store>
E4X dynamic document creation
JavaScript 1.6
var html = <html/>;
html.head.title = "My Page Title";
html.body.@bgcolor = "#e4e4e4";
html.body.form.@name = "myform";
html.body.form.@action = "someurl.jss";
html.body.form.@method = "post";
html.body.form.@onclick = "return somejs();";
html.body.form.input[0] = "";
html.body.form.input[0].@name = "test";
Print(html.toXMLString());
prints:
<html>
<head>
<title>My Page Title</title>
</head>
<body bgcolor="#e4e4e4">
<form name="myform" action="someurl.jss" method="post" onclick="return somejs();">
<input name="test"></input>
</form>
</body>
</html>
E4X dynamic tag name
JavaScript 1.6
var nodeName = 'FOO';
if ( isXMLName(nodeName) ) { // assert that nodeName can be used as a node name
var x = <{nodeName}>test</{nodeName}>
Print( x.toXMLString() ); // prints: <FOO>test</FOO>
}
E4X dynamic content
JavaScript 1.6
var content = 'FOO';
var x = <item>{content}</item>
Print( x.toXMLString() ); // prints: <item>FOO</item>
E4X iteration
JavaScript 1.6
var sales =
<sales vendor="John">
<item type="peas" price="4" quantity="5"/>
<item type="carrot" price="3" quantity="10"/>
<item type="chips" price="5" quantity="3"/>
</sales>;
for each( var price in sales..@price ) {
Print( price + '\n' );
}
prints:
4
3
5
Listen a property for changes
function onFooChange( id, oldval, newval ) {
Print( id + " property changed from " + oldval + " to " + newval );
return newval;
}
var o = { foo:5 };
o.watch( 'foo', onFooChange );
o.foo = 6;
delete o.foo;
o.foo = 7;
o.unwatch('foo');
o.foo = 8;
prints:
foo property changed from 5 to 6
foo property changed from undefined to 7
Logical operators tricks
var a = 5;
a == 5 && Print( 'a is 5 \n' );
a == 7 || Print( 'a is not 7 \n' );
prints:
a is 5
a is not 7
Functions argument default value
function foo( a, b ) {
a = a || '123';
b = b || 55;
Print( a + ',' + b );
}
foo(); // prints: 123,55
foo('bar'); // prints: bar,55
foo('x', 'y'); // prints x,y
but:
foo(0,''); // prints: 123,55
because 0 and '' are evaluated as false !
Remove an item by value in an Array object
var arr = ['a', 'b', 'c', 'd'];
var pos = arr.indexOf( 'c' );
pos > -1 && arr.splice( pos, 1 );
Print( arr ); // prints: a,b,d
Multiple-value returns
JavaScript 1.7
function f() {
return [1, 2];
}
var [a, b] = f();
Print( a + ' ' + b ); // prints: 1 2
Operator [ ] and strings ( like charAt() )
JavaScript 1.6
var str = 'foobar';
Print( str[4] );
prints:
a
indexOf() and lastIndexOf() Works on Array
JavaScript 1.6
var obj = {};
var arr = [ 'foo', 567, obj, 12.34 ];
Print( arr.indexOf(obj) ); // prints: 2
Using Array functions on a non-Array object
JavaScript 1.7
var obj = {};
Array.push(obj, 'foo');
Array.push(obj, 123);
Array.push(obj, 5.55);
Print( obj.toSource() ); // prints: ({0:"foo", length:3, 1:123, 2:5.55})
Change current object (this) of a function call
function test(arg) {
Print( this[0]+' '+this[1]+' '+arg );
}
var arr = ['foo', 'bar'];
test.call(arr, 'toto'); // prints: foo bar toto
Filter / intercept a function call
function bar(a, b, c, d, e, f) {
Print(a, b, c, d, e, f)
}
function foo() {
bar.apply(this, arguments);
}
foo(1, 2, 3, 4, 5, 6); // prints: 123456
E4X Query ( like XPath )
JavaScript 1.6
var xml = <mytree>
<data id="1" text="foo"/>
<data id="2" text="bar"/>
</mytree>
Print( xml.data.(@id==1).@text );
var myid = 2;
Print( xml.data.(@id==myid).@text );
prints:
foo
bar
swap two variables
JavaScript 1.7
var a = 1;
var b = 2;
[a,b] = [b,a];
Destructuring assignment with function arguments
JavaScript 1.7
function foo( [a,b] ) {
Print(a);
Print(b);
}
foo( [12,34] );
Prints:
12
34
JavaScript scope is not C/C++ scope
if ( false ) { // never reach the next line
var foo = 123;
}
Print(foo); // prints: undefined ( but is defined )
Print(bar); // failed: ReferenceError: bar is not defined
JavaScript scope and LET instruction
JavaScript 1.7
var x = 5;
var y = 0;
let (x = x+10, y = 12) {
Print(x+y);
}
Print(x+y);
prints:
27
5
or,
for ( let i=0 ; i < 10 ; i++ ) {
Print(i + ' ');
}
Print(i);
prints:
0 1 2 3 4 5 6 7 8 9 test.js:4: ReferenceError: i is not defined
Defer function calls
var opList = [];
function deferCall( text, value ) {
opList.push(arguments)
}
function doCall(func) {
while (opList.length)
func.apply(this,opList.shift());
}
deferCall( 'one', 1 );
deferCall( 'titi', 'toto' );
deferCall( 5, 'foo' );
function callLater(a,b) {
Print(a+', '+b);
}
doCall( callLater )
Prints:
one, 1
titi, toto
5, foo
Insert an array in another array
var a = [1,2,3,7,8,9]
var b = [4,5,6]
var insertIndex = 3;
a.splice.apply(a, Array.concat(insertIndex, 0, b));
Print(a); // prints: 1,2,3,4,5,6,7,8,9
Multiple string concatenation
var html = ['aaa', 'bbb', 'ccc', ...].join('');
Is faster than:
var html = 'aaa' + 'bbb' + 'ccc' + ...;
This is true with a big number of elements ( > 5000 )
HTTP headers parser
var rexp_keyval = /(.*?): ?(.*?)\r?\n/g;
function headersToObject( allHeaders ) {
var res, hdrObj = {};
for ( rexp_keyval.lastIndex = 0; res = rexp_keyval.exec(allHeaders); hdrObj[res[1]] = res[2])
return hdrObj;
}
Using 'with' scope
with({ a:5 }) function toto() { return a }
toto() // returns 5
(object).toString()
var a = { toString:function() { return '123'; } }
Print(a); // prints '123', and not [Object object]
RegExpr.$1
var re = /a(.*)/
'abcd'.match(re)
Print( RegExp.$1 ) // prints 'bcd'
Binary with XmlHTTPRequest
browser related example
var req = new XMLHttpRequest();
req.open('GET', "http://code.google.com/images/code_sm.png",false);
req.overrideMimeType('text/plain; charset=x-user-defined');
//req.overrideMimeType('application/octet-stream');
req.send(null);
var val = req.responseText;
Print( escape(val.substr(0,10)) );
Iterate on values
JavaScript 1.6
for each ( var i in [3,23,4] )
Print(i)
Prints:
3
23
4
Exceptions Handling / conditional catch (try catch if)
function Toto(){}
function Titi(){}
try {
throw new Titi()
} catch ( err if err instanceof Toto ) {
Print('toto')
} catch ( err if err instanceof Titi ) {
Print('titi')
} catch(ex) {
throw(ex);
}
Special chars
$=4
_=5
Print( _+$)
prints:
9
object's eval method
var foo = { bar:123 };
foo.eval('bar') // returns 123
var foo = { bar:123 };
with ( foo )
var val = eval( 'bar' );
Print( val ); // returns 123
eval this
function test() {
Print(eval('this'));
}
test.call(123)
prints:
123
No Such Method ( noSuchMethod )
var o = {}
o.__noSuchMethod__ = function(arg){ Print('unable to call "'+arg+'" function') }
o.foo(234)
prints:
unable to call "foo" function
RegExp replace
function Replacer( conversionObject ) {
var regexpStr = '';
for ( var k in conversionObject )
regexpStr += (regexpStr.length ? '|' : '') + k;
var regexpr = new RegExp(regexpStr,'ig'); // g: global, m:multi-line i: ignore case
return function(s) { return s.replace(regexpr, function(str, p1, p2, offset, s) { var a = conversionObject[str]; return a == undefined ? str : a }) }
}
var myReplacer = Replacer( { '<BR>':'\n', '&':'&', '<':'<', '>':'>', '"':'"' } );
Print( myReplacer('aa<BR>a &&&<') );
prints:
aa
a &&&<
Values comparison
[4] === 4 // is: false
[4] == 4 // is: true
'0' == 0 // is: true
'0' === 0 // is: false
undefined, null, 0, false, '', ...
var a = { b:undefined, c:null, d:0, f:'' }
a['b'] // is: undefined
a['e'] // is: undefined
'b' in a // is: true
'e' in a // is: false
Boolean(a.b) // is: false
Boolean(a.c) // is: false
Boolean(a.d) // is: false
Boolean(a.e) // is: false !
Boolean(a.f) // is: false
typeof( asvqwfevqwefq ) == 'undefined' // =true
Print( '' == false ); // prints: true
Print( 0 == false ); // prints: true
Print( [] == false ); // prints: true
Print( [] == '' ); // prints: true
Print( '' == 0 ); // prints: true
Print( [] == 0 ); // prints: true
constructor property ( InstanceOf + Type )
var a = {};
a.constructor === Object // is: true
AJAX evaluation
var a = {
b:function() { Print(123) }
}
var body = 'b();';
with(a) { eval(body) }; // Prints 123
Comma operator
var a = 0;
var b = ( a++, 99 );
a // is: 1
b // is: 99
var i = 0;
while( Print('x '), i++<10 )
Print(i + ' ')
prints: x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x
closures pitfall
var a = [];
for ( var i=0; i<10; i++ ) {
a[i] = function() { Print(i); }
}
a[0](); // is: 10
a[1](); // is: 10
a[2](); // is: 10
a[3](); // is: 10
simpler case:
var i = 5;
function foo() { Print(i) }
foo(); // prints 5
i = 6;
foo(); // prints 6
Closure definition
A closure occurs when one function appears entirely within the body of another, and the inner function refers to local variables of the outer function.
links
obligated lecture to understand closures
In the first example, you can avoid the behavior using the let instruction:
var a = [];
for ( var i=0; i<10; i++ ) {
let j = i;
a[i] = function() { Print(j); }
}
In this case, each instances of the (anonymous) inner function refer to different instances of variable j.
sharp variable
var a = { titi:#1={}, toto:#1# };
a.titi === a.toto; // is: true
var a = { b:#1={ c:#1# } }
// a.b.c.c.c.c.c.c...
common object between 2 objects
function a() {}
a.prototype = { b:{} }
c = new a;
d = new a;
c.b.e = 2;
c.b === d.b // is: true !
d.b.e // is: 2
constructor
function a( b ) {
Print(b);
}
c.prototype = new a;
function c( arg ) {
this.constructor.apply( this, arg );
};
o = new c( [1,2,3] );
prints:
undefined
1
JavaScript string can contain null chars
var test = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
test.length // is: 10
'new' operator precedence
function test() {
this.toto = 1234;
}
(new test()).toto // ok ( logical way )
new test().toto // ok
new test.toto // bad !
(new test).toto // ok
constructor usage
function toto( val ) { // parent class
this.print = function() {
Print( val );
}
}
titi.prototype = new toto;
function titi( val ) { // child class
this.constructor(val);
}
(new titi(7)).print(); // prints 7
To object
var obj = Object(123);
obj.foo = 678;
Print( typeof( obj ) + ', ' + obj + ', ' + obj.foo ); // prints: object, 123, 678
Serialize or uneval a variable or an object ( can be nested )
var o = { a:123, b:'test', c:function() { return 987; } }
Print( o.toSource() ); // prints: ({a:123, b:"test", c:(function () {return 987;})})
Print( uneval(o) ); // prints: ({a:123, b:"test", c:(function () {return 987;})})
JavaScript labels
xxx: {
Print( 111 )
break xxx;
Print( 222 )
}
prints:
111
for in loop
for ( var i in function(){ return [1, 2, 3] }() )
Print( i );
prints:
0
1
2
proto ( prototype property )
var a = {}
var b = {}
a.__proto__ == b.__proto__ // is: true
Numbers and floating point error
Print( 1000000000000000128 ); // prints 1000000000000000100
Number base conversion ( hexadecimal )
(255).toString(16); // is: ff
parseInt( 'ff', 16 ) // is: 255
parseInt('0xff'); // is 255
try / catch / finally
try {
} catch(ex) {
Print('catch')
} finally {
Print('finally')
}
prints:
finally
Object argument
var proto = {
x: function() { return 'x' }
}
var o1 = new Object( proto );
var o2 = new Object( proto );
o1 == o2 // is: true
object and its prototype
function obj() {}
obj.prototype = { x:1 };
var b = new obj;
obj.prototype = { x:2 };
var c = new obj;
c.x == b.x // is: false
Runtime prototype object
var proto1 = {
a:function(){ return 1 }
}
var proto2 = {
a:function(){ return 2 }
}
function createObject( proto ) {
var cl = function() {}
cl.prototype = proto;
return new cl;
}
var v1 = createObject( proto1 ).a
var v2 = createObject( proto2 ).a
Print( v1() );
Print( v2() );
Print( createObject( proto1 ).a === createObject( proto1 ).a );
prints:
1
2
true
for in loop and undefined value
var o = { x:undefined }
for ( var i in o )
Print(i)
prints:
x
Call function in parent class
toto.prototype = new function() {
this.a = function() {
Print(456)
}
};
function toto() {
this.a=function(){
Print(123)
toto.prototype.a.call(this); // or: this.__proto__.a();
}
}
var o = new toto;
o.a();
prints:
123
456
getter and setter function
abcd getter = function() {
Print(345);
}
abcd;
abcd;
abcd;
prints:
345
345
345
Beware
The previous syntax used to define a getter is highly deprecated, and should be replaced with:
__defineGetter__('abcd', function() {
Print(345);
});
defineGetter ( define getter )
o = {}
o.__defineGetter__('x', function(){ Print('xxx')} )
o.x
prints:
xxx
check if an object or its prototype has a propery (hasOwnProperty)
var o = { a:1 }
o.__proto__ = { b:2 }
o.hasOwnProperty('a'); // is: true
o.hasOwnProperty('b'); // is: false
check this article about hasOwnProperty function
check if a property is enumerable (propertyIsEnumerable)
var o = { a:1 }
o.propertyIsEnumerable( 'a' ); // is: true
[].propertyIsEnumerable( 'splice' ); // is: false
find the getter/setter function of an object ( lookup getter )
function test() {
this.x getter = function(){ Print( 'getter' ) }
this.x setter = function(){ Print( 'setter' ) }
}
var t = new test
Print( t.__lookupGetter__( 'x' ) );
prints:
function () {
Print("getter");
}
suppress array element while iterating it
the following example will failed :
var a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
for ( var i in a ) {
if ( a[i] == 1 || a[i] == 2 )
a.splice( i, 1 );
}
Print( a ); // prints: 0,2,3,4,5,6,7,8,9
Print( a.length ); // prints: 9
We can use : a[i] == undefined; Or start from the end :
var a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
for ( var i = a.length - 1; i >= 0; i-- ) {
if ( a[i] == 0 || a[i] == 8 )
a.splice( i, 1 );
}
Print( a ); // prints: 1,2,3,4,5,6,7,9
Print( a.length ); // prints: 8
JavaScript arrays
var a = [ 1,2,3 ];
a.x = 'xxx';
for ( var i = 0; i < a.length; i++ )
Print('a: '+a[i]);
for ( var i in a )
Print('b: '+a[i]);
prints:
a: 1
a: 2
a: 3
b: 1
b: 2
b: 3
b: xxx
delete array element
The following do not work:
var ti = [5,6,7,8];
ti.length // is: 4
delete ti[3]
ti.length // is: 4
Use 'splice' instead:
ti.splice(3, 1);
Комментариев нет:
Отправить комментарий