Javascript is little bit different with other programming languages that we learned when we first started studying programming and computer science such as C, Java, C++. It’s mainly because Javascript works asynchronously, and in that reason, people made lots of unfamiliar concepts like callback, hoisting, etc. So I decided to study and post about them.
I mainly referred Learning Advanced Javascript.
Test Function (assert)
Example of Usage
assert( true, "I'll pass." );
assert( "truey", "So will I." );
assert( false, "I'll fail." );
assert( null, "So will I." );
PASS I`ll pass.
PASS So will I.
FAIL I`ll fail.
FAIL So will I.
Defining Functions
Function Declaration
function isNimble(){ return true; }
Function Expression
Anonymous Function Expression
var canFly = function(){ return true; };
Named Function Expression
var canFly = function iCanFly(){ return true; };
Self Invoking Function Expression
(function sayHello() {
alert("hello!");
})();
Function Hoisting
Hoisting
Hoisting means that, If there is a variable declaration statement in somewhere of the valid scope, it is possible to use the variable anywhere in the same scope.
value = 30;
function hoistingExam() {
console.log("value=" + value);
var value = 10;
console.log("value=" + value);
}
hoistingExam();
value=undefined
value=10
Function Hoisting
If the function is declared by function declaration, it can be hoisted.
var canFly = function(){ return true; };
window.isDeadly = function(){ return true; };
assert( isNimble() && canFly() && isDeadly(), "Still works, even though isNimble is moved." );
function isNimble(){ return true; }
PASS Still works, even though isNimble is moved.
However, if the function created in function expression way, it is impossible to hoist it.
assert( typeof canFly == "undefined", "canFly doesn't get that benefit." );
assert( typeof isDeadly == "undefined", "Nor does isDeadly." );
var canFly = function(){ return true; };
window.isDeadly = function(){ return true; };
PASS canFly doesn't get that benefit.
PASS Nor does isDeadly.
Below return statement?
How about this case?
function stealthCheck(){
assert( stealth());
return stealth();
function stealth(){ return true; } // It's under return statement!
}
stealthCheck();
The answer is …
PASS
Name of The Function
var ninja = function myNinja(){
assert( ninja == myNinja, "This function is named two things - at once!" );
};
ninja();
assert( typeof myNinja == "undefined", "But myNinja isn't defined outside of the function." );
PASS This function is named two things - at once!
PASS But myNinja isn't defined outside of the function.
In the code above, myNinja
function has two names because it is a named function expression. You can call that function with the name myNinja
in the body of myNinja
, but in outside, you can’t.
Object
Property
We can set both a variable and a function as a property.
var katana = {
isSharp: true,
use: function(){
this.isSharp = !this.isSharp;
}
};
katana.use();
assert( !katana.isSharp, "Verify the value of isSharp has been changed." );
Function Object
Function is also an object. So we can give properties to functions. The code below is an example of giving a cache property to a funciton.
function isPrime( num ) {
var prime = num != 1; // Everything but 1 can be prime
if( !arguments.callee.cache ) {
arguments.callee.cache = {};
for ( var i = 2; i < num; i++ ) {
if ( num % i == 0 ) {
prime = false;
break;
}
}
arguments.callee.cache[num] = prime;
return prime;
}
else {
return arguments.callee.cache[num];
}
}
assert( isPrime(5), "Make sure the function works, 5 is prime." );
assert( isPrime.cache[5], "Is the answer cached?" );
PASS Make sure the function works, 5 is prime.
PASS Is the answer cached?
Context
A context contains all members and methods of an object.
this
A context can be referred by this
keyword. It’s just same with that of Java.
call
You can call a method of an object with a context that you want to use by call
method.
function add(a, b){
return a + b;
}
assert( add.call(this, 1, 2) == 3, ".call() takes individual arguments" );
PASS .call() takes individual arguments
apply
It’s similar with call
method. It takes parameters in array form.
function add(a, b){
return a + b;
}
apply( add.call(this, [1, 2]) == 3, ".apply() takes an array of arguments" );
PASS .apply() takes an array of arguments
new
new
keyword creates an object. In Javascript, everything is regarded as an object, so if you omit new
keyword by mistake, a catastrophe might be occurred.
function User(first, last){
this.name = first + " " + last;
}
window.name = "Resig";
var user = User("John", name);
assert( name == "John Resig", "The name variable is accidentally overridden." );
PASS The name variable is accidentally overridden.
The result seems same with our intention, but the code allocated “John Resig” to window.name. So if you use window.name later, you can see the value is unintentionally changed.
So, is there any precautions to avoid this problem except of always paying attention not to omit new
? The answer is no. You can avoid risks by inserting a code that checks whether the context is same with arguments.callee
or not every time you define objects.
function User(first, last){
if ( !(this instanceof arguments.callee) )
return new User(first, last);
this.name = first + " " + last;
}
var name = "Resig";
var user = User("John", name);
assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );
PASS This was defined correctly, even if it was by mistake.
PASS The right name was maintained.
Calling Anonymous Functions by Object Property
Example
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );
PASS A single object isn't too bad, either.
Problem And Solution
If the original object is removed, the code doesn’t run like below.
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );
var samurai = { yell: ninja.yell };
var ninja = null;
try {
samurai.yell(4);
} catch(e){
assert( false, "Uh, this isn't good! Where'd ninja.yell go?" );
}
PASS A single object isn't too bad, either.
FAIL Uh, this isn't good! Where'd ninja.yell go?
There are two solutions. First one is to simply name the anonymous function.
var ninja = {
yell: function yell(n){
return n > 0 ? yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" );
var samurai = { yell: ninja.yell };
var ninja = {};
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
PASS Works as we would expect it to!
PASS The method correctly calls itself.
Second one is to use argument.callee()
for the function to call itself except of the function name.
var ninja = {
yell: function(n){
return n > 0 ? arguments.callee(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "arguments.callee is the function itself." );
PASS arguments.callee is the function itself.
arguments
You can define a function that accepts unspecified number of arguments with the keyword arguments
.
function smallest(){
return Math.min.apply( Math/* It doesn't have to be Math */, arguments );
}
function largest(){
return Math.max.apply( this/* It can be any context you want */, arguments );
}
assert(smallest(0, 1, 2, 3) == 0, "Locate the smallest value.");
assert(largest(0, 1, 2, 3) == 3, "Locate the largest value.");
PASS Locate the smallest value.
PASS Locate the largest value.
Closures
Definition
Inner functions can access member variables and parameters of the outer function. Scope chains that follow this rule are called closures.
var num = 10;
function addNum(myNum){
return num + myNum;
}
num = 15;
assert( addNum(5) == 15, "Add two numbers together, one from a closure." );
FAIL Add two numbers together, one from a closure.
addNum
adds two variables and num
is from a closure. num
is originally set as 10, but before addNum
is called, the value of it is changed to 15. So the result of addNum(5) would be 20, not 15.
Additional Rule
A closure doesn’t disappear even after the outer function returns.
function celebrityName(firstName) {
var nameIntro = "This celebrity is ";
function lastName(theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName("Michael");
assert(mjName("Jackson") == "This celebrity is Michael Jackson", "Function lastName had been saved into a variable mjName and was called after celebrityName returned.");
PASS Function lastName had been saved into a variable mjName and was called after celebrityName returned.
Test
var a = 5;
function runMe(a){
assert( a == ___, "Check the value of a." );
function innerRun(){
assert( b == ___, "Check the value of b." );
assert( c == ___, "Check the value of c." );
}
var b = 7;
innerRun();
var c = 8;
}
runMe(6);
for ( var d = 0; d < 3; d++ ) {
setTimeout(function(){
assert( d == ___, "Check the value of d." );
}, 100);
}
댓글
댓글 쓰기