-
A re-introduction to JavaScript (JStutorial)by109contributors: Showall
IntroductionWhyareintroduction?BecauseJavaScriptisnotoriousforbeing theworld'smostmisunderstood
programminglanguage.Whileitisoftenderidedasatoy,beneathitsdeceptivesimplicityliesomepowerful
languagefeatures.JavaScriptisnowusedbyanincrediblenumberofhighprofileapplications,showingthat
deeperknowledgeofthistechnologyisanimportantskillforanywebormobiledeveloper.
It'susefultostartwithanoverviewofthelanguage'shistory.JavaScriptwascreatedin1995byBrendan
Eich,anengineeratNetscape,andfirstreleasedwithNetscape2earlyin1996.(Itwasoriginallygoingtobe
calledLiveScript,butwasrenamedinanillfatedmarketingdecisioninanattempttocapitalizeonthe
popularityofSunMicrosystem'sJavalanguagedespitethetwohavingverylittleincommon.Thishas
beenasourceofconfusioneversince.)
Severalmonthslater,MicrosoftreleasedJScript,amostlycompatibleJavaScriptworkalike,withInternet
Explorer3.Severalmonthsafterthat,NetscapesubmittedJavaScriptto EcmaInternational,aEuropean
standardsorganization,whichresultedinthefirsteditionoftheECMAScriptstandardthatyear.The
standardreceivedasignificantupdateas ECMAScriptedition3in1999,andhasstayedprettymuchstable
eversince.Thefourtheditionwasabandoned,duetopoliticaldifferencesconcerninglanguagecomplexity.
ManypartsofthefourtheditionformedthebasisforECMAScriptedition5,publishedinDecemberof2009,
andforthe6thmajoreditionofthestandard,waspublishedinJuneof2015.
Forfamiliarity,IwillrefertoECMAScriptas"JavaScript"fromthispointon.
Unlikemostprogramminglanguages,theJavaScriptlanguagehasnoconceptofinputoroutput.Itis
designedtorunasascriptinglanguageinahostenvironment,anditisuptothehostenvironmentto
providemechanismsforcommunicatingwiththeoutsideworld.Themostcommonhostenvironmentisthe
browser,butJavaScriptinterpreterscanalsobefoundinahugelistofotherplaces,includingAdobe
Acrobat,AbobePhotoshop,SVGimages,Yahoo'sWidgetengine,serversideenvironmentssuchas
Node.js,NoSQLdatabasesliketheopensource ApacheCouchDB,embeddedcomputers,complete
desktopenvironmentslike GNOME(oneofthemostpopularGUIsforGNU/Linuxoperatingsystems),and
-
thelistgoeson.
OverviewJavaScriptisanobjectorienteddynamiclanguagewithtypesandoperators,standardbuiltinobjects,and
methods.ItssyntaxcomesfromtheJavaandClanguages,somanystructuresfromthoselanguagesapply
toJavaScriptaswell.OneofthekeydifferencesisthatJavaScriptdoesnothaveclassesinstead,theclass
functionalityisaccomplishedbyobjectprototypes.Theothermaindifferenceisthatfunctionsareobjects,
givingfunctionsthecapacitytoholdexecutablecodeandbepassedaroundlikeanyotherobject.
Let'sstartoffbylookingatthebuildingblockofanylanguage:thetypes.JavaScriptprogramsmanipulate
values,andthosevaluesallbelongtoatype.JavaScript'stypesare:
Number
String
Boolean
Function
Object
Symbol(newinEdition6)
...oh,andundefinedandnull,whichare...slightlyodd.AndArray,whichareaspecialkindofobject.And
DateandRegExp,whichareobjectsthatyougetforfree.Andtobetechnicallyaccurate,functionsarejusta
specialtypeofobject.Sothetypediagramlooksmorelikethis:
Number
String
Boolean
Symbol(newinEdition6)
Object
Function
Array
Date
RegExp
null
undefined
-
AndtherearesomebuiltinErrortypesaswell.Thingsarealoteasierifwestickwiththefirstdiagram,
though.
NumbersNumbersinJavaScriptare"doubleprecision64bitformatIEEE754values",accordingtothespec.Thishas
someinterestingconsequences.There'snosuchthingasanintegerinJavaScript,soyouhavetobealittle
carefulwithyourarithmeticifyou'reusedtomathinCorJava.Watchoutforstufflike:
Inpractice,integervaluesaretreatedas32bitints(andarestoredthatwayinsomebrowser
implementations),whichcanbeimportantforbitwiseoperations.
Thestandardarithmeticoperatorsaresupported,includingaddition,subtraction,modulus(orremainder)
arithmeticandsoforth.There'salsoabuiltinobjectthatIforgottomentionearliercalledMathifyouwantto
performmoreadvancedmathematicalfunctionsandconstants:
YoucanconvertastringtoanintegerusingthebuiltinparseInt()function.Thistakesthebaseforthe
conversionasanoptionalsecondargument,whichyoushouldalwaysprovide:
Ifyoudon'tprovidethebase,youcangetsurprisingresultsinolderbrowsers(pre2013):
ThathappenedbecausetheparseInt()functiondecidedtotreatthestringasoctalduetotheleading0.
Ifyouwanttoconvertabinarynumbertoaninteger,justchangethebase:
0.1+0.2==0.300000000000000041
Math.sin(3.5);
varcircumference=Math.PI*(r+r);
1
2
parseInt("123",10);//123
parseInt("010",10);//10
1
2
parseInt("010");//81
parseInt("11",2);//31
-
Similarly,youcanparsefloatingpointnumbersusingthebuiltinparseFloat()functionwhichusesbase10alwaysunlikeitsparseInt()cousin.
Youcanalsousetheunary+operatortoconvertvaluestonumbers:
AspecialvaluecalledNaN(shortfor"NotaNumber")isreturnedifthestringisnonnumeric:
NaNistoxic:ifyouprovideitasaninputtoanymathematicaloperationtheresultwillalsobeNaN:
YoucantestforNaNusingthebuiltinisNaN()function:
JavaScriptalsohasthespecialvaluesInfinityandInfinity:
YoucantestforInfinity,InfinityandNaNvaluesusingthebuiltinisFinite()function:
Note: The parseInt() and parseFloat() functions parse a string until they reach a character that
isn'tvalidforthespecifiednumberformat,thenreturnthenumberparseduptothatpoint.Howeverthe"+"operator
+"42";//421
parseInt("hello",10);//NaN1
NaN+5;//NaN1
isNaN(NaN);//true1
1/0;//Infinity
1/0;//Infinity
1
2
isFinite(1/0);//false
isFinite(Infinity);//false
isFinite(NaN);//false
1
2
3
-
simplyconvertsthestringtoNaNifthereisanyinvalidcharacterinit.Justtryparsingthestring"10.2abc"witheach
methodbyyourselfintheconsoleandyou'llunderstandthedifferencesbetter.
StringsStringsinJavaScriptaresequencesofcharacters.Moreaccurately,theyaresequencesofUnicodecharacters,witheachcharacterrepresentedbya16bitnumber.Thisshouldbewelcomenewstoanyonewhohashadtodealwithinternationalization.
Ifyouwanttorepresentasinglecharacter,youjustuseastringoflength1.
Tofindthelengthofastring,accessitslengthproperty:
There'sourfirstbrushwithJavaScriptobjects!DidImentionthatyoucanusestringslikeobjectstoo?Theyhavemethodsaswellthatallowyoutomanipulatethestringandaccessinformationaboutthestring:
OthertypesJavaScriptdistinguishesbetweennull,whichisavaluethatindicatesadeliberatenonvalue(andisonlyaccessiblethroughthenullkeyword),andundefined,whichisavalueoftype'undefined'thatindicatesanuninitializedvaluethatis,avaluehasn'tevenbeenassignedyet.We'lltalkaboutvariableslater,butinJavaScriptitispossibletodeclareavariablewithoutassigningavaluetoit.Ifyoudothis,thevariable'stypeisundefined.undefinedisactuallyaconstant.
JavaScripthasabooleantype,withpossiblevaluestrueandfalse(bothofwhicharekeywords).Anyvaluecanbeconvertedtoabooleanaccordingtothefollowingrules:
1. false,0,theemptystring(""),NaN,null,andundefinedallbecomefalse.
2. allothervaluesbecometrue.
YoucanperformthisconversionexplicitlyusingtheBoolean()function:
"hello".length;//51
"hello".charAt(0);//"h"
"hello,world".replace("hello","goodbye");//"goodbye,world"
"hello".toUpperCase();//"HELLO"
1
2
3
-
However,thisisrarelynecessary,asJavaScriptwillsilentlyperformthisconversionwhenitexpectsa
boolean,suchasinanifstatement(seebelow).Forthisreason,wesometimesspeaksimplyof"true
values"and"falsevalues,"meaningvaluesthatbecometrueandfalse,respectively,whenconvertedto
booleans.Alternatively,suchvaluescanbecalled"truthy"and"falsy",respectively.
Booleanoperationssuchas&&(logicaland),||(logicalor),and!(logicalnot)aresupportedseebelow.
Variables
NewvariablesinJavaScriptaredeclaredusingthevarkeyword:
Ifyoudeclareavariablewithoutassigninganyvaluetoit,itstypeisundefined.
AnimportantdifferencefromotherlanguageslikeJavaisthatinJavaScript,blocksdonothavescopeonly
functionshavescope.Soifavariableisdefinedusingvarinacompoundstatement(forexampleinsidean
ifcontrolstructure),itwillbevisibletotheentirefunction.However,startingwithECMAScriptEdition6,let
andconstdeclarationsallowyoutocreateblockscopedvariables.
Operators
JavaScript'snumericoperatorsare+,,*,/and%whichistheremainderoperator.Valuesareassigned
using=,andtherearealsocompoundassignmentstatementssuchas+=and=.Theseextendouttox=
xoperatory.
Youcanuse++andtoincrementanddecrementrespectively.Thesecanbeusedasprefixorpostfix
operators.
The+operatoralsodoesstringconcatenation:
Boolean("");//falseBoolean(234);//true
1
2
vara;varname="simon";
1
2
x+=5x=x+5
1
2
-
Ifyouaddastringtoanumber(orothervalue)everythingisconvertedintoastringfirst.Thismightcatch
youup:
Addinganemptystringtosomethingisausefulwayofconvertingit.
ComparisonsinJavaScriptcanbemadeusing,=.Theseworkforbothstringsandnumbers.
Equalityisalittlelessstraightforward.Thedoubleequalsoperatorperformstypecoercionifyougiveit
differenttypes,withsometimesinterestingresults:
Toavoidtypecoercion,usethetripleequalsoperator:
Therearealso!=and!==operators.
JavaScriptalsohasbitwiseoperations.Ifyouwanttousethem,they'rethere.
ControlstructuresJavaScripthasasimilarsetofcontrolstructurestootherlanguagesintheCfamily.Conditionalstatements
aresupportedbyifandelseyoucanchainthemtogetherifyoulike:
"hello"+"world";//"helloworld"1
"3"+4+5;//"345"
3+4+"5";//"75"
1
2
"dog"=="dog";//true
1==true;//true
1
2
1===true;//false
true===true;//true
1
2
varname="kittens";
if(name=="puppies"){
name+="!";
}elseif(name=="kittens"){
name+="!!";
}else{
1
2
3
4
5
6
-
JavaScripthaswhileloopsanddowhileloops.Thefirstisgoodforbasicloopingthesecondforloops
whereyouwishtoensurethatthebodyoftheloopisexecutedatleastonce:
JavaScript'sforloopisthesameasthatinCandJava:itletsyouprovidethecontrolinformationforyour
looponasingleline.
The&&and||operatorsuseshortcircuitlogic,whichmeanswhethertheywillexecutetheirsecondoperand
isdependentonthefirst.Thisisusefulforcheckingfornullobjectsbeforeaccessingtheirattributes:
Orforsettingdefaultvalues:
JavaScripthasaternaryoperatorforconditionalexpressions:
name="!"+name;}name=="kittens!!"
7
8
9
while(true){//aninfiniteloop!}
varinput;do{input=get_input();}while(inputIsNotValid(input))
1
2
3
4
5
6
7
8
for(vari=0;i18)?"yes":"no";1
-
Theswitchstatementcanbeusedformultiplebranchesbasedonanumberorstring:
Ifyoudon'taddabreakstatement,executionwill"fallthrough"tothenextlevel.Thisisveryrarelywhatyouwantinfactit'sworthspecificallylabelingdeliberatefallthroughwithacommentifyoureallymeantittoaiddebugging:
Thedefaultclauseisoptional.Youcanhaveexpressionsinboththeswitchpartandthecasesifyoulikecomparisonstakeplacebetweenthetwousingthe===operator:
ObjectsJavaScriptobjectscanbethoughtofassimplecollectionsofnamevaluepairs.Assuch,theyaresimilarto:
switch(action){
case'draw':
drawIt();
break;
case'eat':
eatIt();
break;
default:
doNothing();
}
1
2
3
4
5
6
7
8
9
10
switch(a){
case1://fallthrough
case2:
eatIt();
break;
default:
doNothing();
}
1
2
3
4
5
6
7
8
switch(1+3){
case2+2:
yay();
break;
default:
neverhappens();
}
1
2
3
4
5
6
7
-
DictionariesinPython
HashesinPerlandRuby
HashtablesinCandC++
HashMapsinJava
AssociativearraysinPHP
Thefactthatthisdatastructureissowidelyusedisatestamenttoitsversatility.Sinceeverything(barcoretypes)inJavaScriptisanobject,anyJavaScriptprogramnaturallyinvolvesagreatdealofhashtablelookups.It'sagoodthingthey'resofast!
The"name"partisaJavaScriptstring,whilethevaluecanbeanyJavaScriptvalueincludingmoreobjects.Thisallowsyoutobuilddatastructuresofarbitrarycomplexity.
Therearetwobasicwaystocreateanemptyobject:
And:
Thesearesemanticallyequivalentthesecondiscalledobjectliteralsyntax,andismoreconvenient.ThissyntaxisalsothecoreofJSONformatandshouldbepreferredatalltimes.
Objectliteralsyntaxcanbeusedtoinitializeanobjectinitsentirety:
Attributeaccesscanbechainedtogether:
varobj=newObject();1
varobj={};1
varobj={
name:"Carrot",
"for":"Max",
details:{
color:"orange",
size:12
}
}
1
2
3
4
5
6
7
8
-
Thefollowingexamplecreatesanobjectprototype,Person,andinstanceofthatprototype,You.
Oncecreated,anobject'spropertiescanagainbeaccessedinoneoftwoways:
And...
Thesearealsosemanticallyequivalent.Thesecondmethodhastheadvantagethatthenameofthe
propertyisprovidedasastring,whichmeansitcanbecalculatedatruntimethoughusingthismethod
preventssomeJavaScriptengineandminifieroptimizationsbeingapplied.Itcanalsobeusedtosetandget
propertieswithnamesthatarereservedwords:
Note:StartingfromEcmaScript5,reservedwordsmaybeusedasobjectpropertynames"inthebuff".
Thismeansthattheydon'tneedtobe"clothed"inquoteswhendefiningobjectliterals.SeeES5
Spec.
obj.details.color;//orangeobj["details"]["size"];//12
1
2
functionPerson(name,age){this.name=name;this.age=age;}
//DefineanobjectvarYou=newPerson("You",24);//Wearecreatinganewpersonnamed"You"//(thatwasthefirstparameter,andtheage..)
1
2
3
4
5
6
7
8
9
obj.name="Simon";varname=obj.name;
1
2
obj["name"]="Simon";varname=obj["name"];
1
2
obj.for="Simon";//Syntaxerror,because'for'isareservedwordobj["for"]="Simon";//worksfine
1
2
-
Formoreonobjectsandprototypessee:Object.prototype.
ArraysArraysinJavaScriptareactuallyaspecialtypeofobject.Theyworkverymuchlikeregularobjects
(numericalpropertiescannaturallybeaccessedonlyusing[]syntax)buttheyhaveonemagicproperty
called'length'.Thisisalwaysonemorethanthehighestindexinthearray.
Onewayofcreatingarraysisasfollows:
Amoreconvenientnotationistouseanarrayliteral:
Notethatarray.lengthisn'tnecessarilythenumberofitemsinthearray.Considerthefollowing:
Rememberthelengthofthearrayisonemorethanthehighestindex.
Ifyouqueryanonexistentarrayindex,yougetundefined:
Ifyoutaketheaboveintoaccount,youcaniterateoveranarrayusingthefollowing:
vara=newArray();a[0]="dog";a[1]="cat";a[2]="hen";a.length;//3
1
2
3
4
5
vara=["dog","cat","hen"];a.length;//3
1
2
vara=["dog","cat","hen"];a[100]="fox";a.length;//101
1
2
3
typeofa[90];//undefined1
for(vari=0;i
-
Thisisslightlyinefficientasyouarelookingupthelengthpropertyonceeveryloop.Animprovementisthis:
Anicerlookingbutlimitedidiomis:
Herewearesettinguptwovariables.Theassignmentinthemiddlepartoftheforloopisalsotestedfor
truthfulnessifitsucceeds,theloopcontinues.Sinceiisincrementedeachtime,itemsfromthearraywill
beassignedtoiteminsequentialorder.Theloopstopswhena"falsy"itemisfound(suchasundefined).
Thistrickshouldonlybeusedforarrayswhichyouknowdonotcontain"falsy"values(arraysofobjectsorDOMnodesforexample).Ifyouareiteratingovernumericdatathatmightincludea0orstringdatathatmightincludetheemptystringyoushouldusethei,lenidiominstead.
Youcaniterateoveranarrayusingafor...inloop.Notethatifsomeoneaddednewpropertiesto
Array.prototype,theywillalsobeiteratedoverbythisloop.Thereforethismethodis"not"recommended.
AnotherwayofiteratingoveranarraythatwasaddedwithECMAScript5isforEach():
Ifyouwanttoappendanitemtoanarraysimplydoitlikethis:
Arrayscomewithanumberofmethods.Seealsothefulldocumentationforarraymethods.
}3
for(vari=0,len=a.length;i
-
Methodname Description
a.toString()ReturnsastringwiththetoString()ofeachelement
separatedbycommas.
a.toLocaleString()ReturnsastringwiththetoLocaleString()ofeach
elementseparatedbycommas.
a.concat(item1[,item2[,...[,
itemN]]])Returnsanewarraywiththeitemsaddedontoit.
a.join(sep)Convertsthearraytoastringvaluesdelimitedbythesep
param
a.pop() Removesandreturnsthelastitem.
a.push(item1,...,itemN) Pushaddsoneormoreitemstotheend.
a.reverse() Reversethearray.
a.shift() Removesandreturnsthefirstitem.
a.slice(start,end) Returnsasubarray.
a.sort([cmpfn]) Takesanoptionalcomparisonfunction.
a.splice(start,delcount[,item1[,
...[,itemN]]])
Letsyoumodifyanarraybydeletingasectionand
replacingitwithmoreitems.
a.unshift([item]) Prependsitemstothestartofthearray.
FunctionsAlongwithobjects,functionsarethecorecomponentinunderstandingJavaScript.Themostbasicfunction
couldn'tbemuchsimpler:
Thisdemonstratesabasicfunction.AJavaScriptfunctioncantake0ormorenamedparameters.The
functionbodycancontainasmanystatementsasyoulike,andcandeclareitsownvariableswhicharelocal
tothatfunction.Thereturnstatementcanbeusedtoreturnavalueatanytime,terminatingthefunction.If
functionadd(x,y){vartotal=x+y;returntotal;}
1
2
3
4
-
noreturnstatementisused(oranemptyreturnwithnovalue),JavaScriptreturnsundefined.
Thenamedparametersturnouttobemorelikeguidelinesthananythingelse.Youcancallafunctionwithoutpassingtheparametersitexpects,inwhichcasetheywillbesettoundefined.
Youcanalsopassinmoreargumentsthanthefunctionisexpecting:
Thatmayseemalittlesilly,butfunctionshaveaccesstoanadditionalvariableinsidetheirbodycalledarguments,whichisanarraylikeobjectholdingallofthevaluespassedtothefunction.Let'srewritetheaddfunctiontotakeasmanyvaluesaswewant:
That'sreallynotanymoreusefulthanwriting2+3+4+5though.Let'screateanaveragingfunction:
add();//NaN
//Youcan'tperformadditiononundefined
1
2
add(2,3,4);//5
//addedthefirsttwo;4wasignored
1
2
functionadd(){
varsum=0;
for(vari=0,j=arguments.length;i
-
Thisisprettyuseful,butintroducesanewproblem.Theavg()functiontakesacommaseparatedlistof
argumentsbutwhatifyouwanttofindtheaverageofanarray?Youcouldjustrewritethefunctionas
follows:
Butitwouldbenicetobeabletoreusethefunctionthatwe'vealreadycreated.Luckily,JavaScriptletsyou
callafunctionandcallitwithanarbitraryarrayofarguments,usingtheapply()methodofanyfunction
object.
Thesecondargumenttoapply()isthearraytouseasargumentsthefirstwillbediscussedlateron.This
emphasizesthefactthatfunctionsareobjectstoo.
JavaScriptletsyoucreateanonymousfunctions.
Thisissemanticallyequivalenttothefunctionavg()form.It'sextremelypowerful,asitletsyouputafull
functiondefinitionanywherethatyouwouldnormallyputanexpression.Thisenablesallsortsofclever
tricks.Here'sawayof"hiding"somelocalvariableslikeblockscopeinC:
functionavgArray(arr){
varsum=0;
for(vari=0,j=arr.length;i
-
JavaScriptallowsyoutocallfunctionsrecursively.Thisisparticularlyusefulfordealingwithtreestructures,
suchasyougetinthebrowserDOM.
Thishighlightsapotentialproblemwithanonymousfunctions:howdoyoucallthemrecursivelyiftheydon't
haveaname?JavaScriptletsyounamefunctionexpressionsforthis.YoucanusenamedIIFEs
(ImmediatelyInvokedFunctionExpressions)asbelow:
Thenameprovidedtoafunctionexpressionasaboveisonlyavailabletothefunction'sownscope.This
bothallowsmoreoptimizationstobedonebytheengineandamorereadablecode.Thenamealsoshows
upinthedebuggerandsomestacktraceswhichcansaveyoutime.
(function(){
varb=3;
a+=b;
})();
a;//4
b;//2
3
4
5
6
7
8
9
10
functioncountChars(elm){
if(elm.nodeType==3){//TEXT_NODE
returnelm.nodeValue.length;
}
varcount=0;
for(vari=0,child;child=elm.childNodes[i];i++){
count+=countChars(child);
}
returncount;
}
1
2
3
4
5
6
7
8
9
10
varcharsInBody=(functioncounter(elm){
if(elm.nodeType==3){//TEXT_NODE
returnelm.nodeValue.length;
}
varcount=0;
for(vari=0,child;child=elm.childNodes[i];i++){
count+=counter(child);
}
returncount;
})(document.body);
1
2
3
4
5
6
7
8
9
10
-
NotethatJavaScriptfunctionsarethemselvesobjectsandyoucanaddorchangepropertiesonthemjust
likeonobjectswe'veseenintheObjectssection.
Customobjects
Note: For a more detailed discussion of object-oriented programming in JavaScript, see Introduction to
ObjectOrientedJavaScript.
InclassicObjectOrientedProgramming,objectsarecollectionsofdataandmethodsthatoperateonthat
data.JavaScriptisaprototypebasedlanguagewhichcontainsnoclassstatement,suchasisfoundinC++
orJava.(Thisissometimesconfusingforprogrammersaccustomedtolanguageswithaclassstatement.)
Instead,JavaScriptusesfunctionsasclasses.Let'sconsiderapersonobjectwithfirstandlastnamefields.
Therearetwowaysinwhichthenamemightbedisplayed:as"firstlast"oras"last,first".Usingthe
functionsandobjectsthatwe'vediscussedpreviously,here'sonewayofdoingit:
Thisworks,butit'sprettyugly.Youendupwithdozensoffunctionsinyourglobalnamespace.Whatwe
reallyneedisawaytoattachafunctiontoanobject.Sincefunctionsareobjects,thisiseasy:
functionmakePerson(first,last){
return{
first:first,
last:last
};
}
functionpersonFullName(person){
returnperson.first+''+person.last;
}
functionpersonFullNameReversed(person){
returnperson.last+','+person.first;
}
s=makePerson("Simon","Willison");
personFullName(s);//"SimonWillison"
personFullNameReversed(s);"Willison,Simon"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
functionmakePerson(first,last){
return{
first:first,
last:last,
fullName:function(){
returnthis.first+''+this.last;
},
1
2
3
4
5
6
7
-
There'ssomethingherewehaven'tseenbefore:thethiskeyword.Usedinsideafunction,thisreferstothecurrentobject.Whatthatactuallymeansisspecifiedbythewayinwhichyoucalledthatfunction.Ifyoucalleditusingdotnotationorbracketnotationonanobject,thatobjectbecomesthis.Ifdotnotationwasn'tusedforthecall,thisreferstotheglobalobject.
Notethatthisisafrequentcauseofmistakes.Forexample:
WhenwecallfullName()alone,withoutusings.fullName(),thisisboundtotheglobalobject.Sincetherearenoglobalvariablescalledfirstorlastwegetundefinedforeachone.
WecantakeadvantageofthethiskeywordtoimproveourmakePersonfunction:
Wehaveintroducedanotherkeyword:new.newisstronglyrelatedtothis.Whatitdoesisitcreatesabrandnewemptyobject,andthencallsthefunctionspecified,withthissettothatnewobject.Noticethoughthat
fullNameReversed:function(){
returnthis.last+','+this.first;
}
};
}
s=makePerson("Simon","Willison")
s.fullName();//"SimonWillison"
s.fullNameReversed();//"Willison,Simon"
8
9
10
11
12
13
14
15
16
s=makePerson("Simon","Willison");
varfullName=s.fullName;
fullName();//undefinedundefined
1
2
3
functionPerson(first,last){
this.first=first;
this.last=last;
this.fullName=function(){
returnthis.first+''+this.last;
};
this.fullNameReversed=function(){
returnthis.last+','+this.first;
};
}
vars=newPerson("Simon","Willison");
1
2
3
4
5
6
7
8
9
10
11
-
thefunctionspecifiedwiththisdoesnotreturnavaluebutmerelymodifiesthethisobject.It'snewthat
returnsthethisobjecttothecallingsite.Functionsthataredesignedtobecalledbynewarecalled
constructorfunctions.Commonpracticeistocapitalizethesefunctionsasaremindertocallthemwithnew.
TheimprovedfunctionstillhasthesamepitfallwithcallingfullName()alone.
Ourpersonobjectsaregettingbetter,buttherearestillsomeuglyedgestothem.Everytimewecreatea
personobjectwearecreatingtwobrandnewfunctionobjectswithinitwouldn'titbebetterifthiscode
wasshared?
That'sbetter:wearecreatingthemethodfunctionsonlyonce,andassigningreferencestotheminsidethe
constructor.Canwedoanybetterthanthat?Theanswerisyes:
Person.prototypeisanobjectsharedbyallinstancesofPerson.Itformspartofalookupchain(thathasa
specialname,"prototypechain"):anytimeyouattempttoaccessapropertyofPersonthatisn'tset,
JavaScriptwillcheckPerson.prototypetoseeifthatpropertyexiststhereinstead.Asaresult,anything
assignedtoPerson.prototypebecomesavailabletoallinstancesofthatconstructorviathethisobject.
functionpersonFullName(){
returnthis.first+''+this.last;
}
functionpersonFullNameReversed(){
returnthis.last+','+this.first;
}
functionPerson(first,last){
this.first=first;
this.last=last;
this.fullName=personFullName;
this.fullNameReversed=personFullNameReversed;
}
1
2
3
4
5
6
7
8
9
10
11
12
functionPerson(first,last){
this.first=first;
this.last=last;
}
Person.prototype.fullName=function(){
returnthis.first+''+this.last;
};
Person.prototype.fullNameReversed=function(){
returnthis.last+','+this.first;
};
1
2
3
4
5
6
7
8
9
10
-
Thisisanincrediblypowerfultool.JavaScriptletsyoumodifysomething'sprototypeatanytimeinyour
program,whichmeansyoucanaddextramethodstoexistingobjectsatruntime:
Interestingly,youcanalsoaddthingstotheprototypeofbuiltinJavaScriptobjects.Let'saddamethodto
Stringthatreturnsthatstringinreverse:
Ournewmethodevenworksonstringliterals!
AsImentionedbefore,theprototypeformspartofachain.TherootofthatchainisObject.prototype,
whosemethodsincludetoString()itisthismethodthatiscalledwhenyoutrytorepresentanobjectas
astring.ThisisusefulfordebuggingourPersonobjects:
s=newPerson("Simon","Willison");
s.firstNameCaps();//TypeErroronline1:s.firstNameCapsisnotafunction
Person.prototype.firstNameCaps=function(){
returnthis.first.toUpperCase()
};
s.firstNameCaps();//"SIMON"
1
2
3
4
5
6
7
vars="Simon";
s.reversed();//TypeErroronline1:s.reversedisnotafunction
String.prototype.reversed=function(){
varr="";
for(vari=this.length1;i>=0;i){
r+=this[i];
}
returnr;
};
s.reversed();//nomiS
1
2
3
4
5
6
7
8
9
10
11
12
"Thiscannowbereversed".reversed();//desreverebwonnacsihT1
vars=newPerson("Simon","Willison");
s;//[objectObject]
Person.prototype.toString=function(){
return'';
1
2
3
4
5
-
Rememberhowavg.apply()hadanullfirstargument?Wecanrevisitthatnow.Thefirstargumenttoapply()istheobjectthatshouldbetreatedas'this'.Forexample,here'satrivialimplementationofnew:
Thisisn'tanexactreplicaofnewasitdoesn'tsetuptheprototypechain(itwouldbedifficulttoillustrate).Thisisnotsomethingyouuseveryoften,butit'susefultoknowabout.Inthissnippet,...args(includingtheellipsis)iscalledthe"restarguments"asthenameimplies,thiscontainstherestofthearguments.
Calling
isthereforealmostequivalentto
apply()hasasisterfunctionnamedcall,whichagainletsyousetthisbuttakesanexpandedargumentlistasopposedtoanarray.
Innerfunctions
}
s.toString();//""
6
7
8
functiontrivialNew(constructor,...args){
varo={};//Createanobject
constructor.apply(o,args);
returno;
}
1
2
3
4
5
varbill=trivialNew(Person,"William","Orange");1
varbill=newPerson("William","Orange");1
functionlastNameCaps(){
returnthis.last.toUpperCase();
}
vars=newPerson("Simon","Willison");
lastNameCaps.call(s);
//Isthesameas:
s.lastNameCaps=lastNameCaps;
s.lastNameCaps();
1
2
3
4
5
6
7
8
-
InnerfunctionsJavaScriptfunctiondeclarationsareallowedinsideotherfunctions.We'veseenthisoncebefore,withanearliermakePerson()function.AnimportantdetailofnestedfunctionsinJavaScriptisthattheycanaccessvariablesintheirparentfunction'sscope:
Thisprovidesagreatdealofutilityinwritingmoremaintainablecode.Ifafunctionreliesononeortwootherfunctionsthatarenotusefultoanyotherpartofyourcode,youcannestthoseutilityfunctionsinsidethefunctionthatwillbecalledfromelsewhere.Thiskeepsthenumberoffunctionsthatareintheglobalscopedown,whichisalwaysagoodthing.
Thisisalsoagreatcountertothelureofglobalvariables.Whenwritingcomplexcodeitisoftentemptingtouseglobalvariablestosharevaluesbetweenmultiplefunctionswhichleadstocodethatishardtomaintain.Nestedfunctionscansharevariablesintheirparent,soyoucanusethatmechanismtocouplefunctionstogetherwhenitmakessensewithoutpollutingyourglobalnamespace'localglobals'ifyoulike.Thistechniqueshouldbeusedwithcaution,butit'sausefulabilitytohave.
ClosuresThisleadsustooneofthemostpowerfulabstractionsthatJavaScripthastoofferbutalsothemostpotentiallyconfusing.Whatdoesthisdo?
ThenameofthemakeAdderfunctionshouldgiveitaway:itcreatesnew'adder'functions,whichwhencalled
functionbetterExampleNeeded(){vara=1;functiononeMoreThanA(){returna+1;}returnoneMoreThanA();}
1
2
3
4
5
6
7
functionmakeAdder(a){returnfunction(b){returna+b;};}varx=makeAdder(5);vary=makeAdder(20);x(6);//?y(7);//?
1
2
3
4
5
6
7
8
9
-
withoneargumentaddittotheargumentthattheywerecreatedwith.
What'shappeninghereisprettymuchthesameaswashappeningwiththeinnerfunctionsearlieron:afunctiondefinedinsideanotherfunctionhasaccesstotheouterfunction'svariables.Theonlydifferencehereisthattheouterfunctionhasreturned,andhencecommonsensewouldseemtodictatethatitslocalvariablesnolongerexist.Buttheydostillexistotherwisetheadderfunctionswouldbeunabletowork.What'smore,therearetwodifferent"copies"ofmakeAdder'slocalvariablesoneinwhichais5andone
inwhichais20.Sotheresultofthosefunctioncallsisasfollows:
Here'swhat'sactuallyhappening.WheneverJavaScriptexecutesafunction,a'scope'objectiscreatedtoholdthelocalvariablescreatedwithinthatfunction.Itisinitialisedwithanyvariablespassedinasfunctionparameters.Thisissimilartotheglobalobjectthatallglobalvariablesandfunctionslivein,butwithacoupleofimportantdifferences:firstly,abrandnewscopeobjectiscreatedeverytimeafunctionstartsexecuting,andsecondly,unliketheglobalobject(whichisaccessibleasthisandinbrowsersisaccessibleaswindow)
thesescopeobjectscannotbedirectlyaccessedfromyourJavaScriptcode.Thereisnomechanismforiteratingoverthepropertiesofthecurrentscopeobject,forexample.
SowhenmakeAdderiscalled,ascopeobjectiscreatedwithoneproperty:a,whichistheargumentpassed
tothemakeAdderfunction.makeAdderthenreturnsanewlycreatedfunction.NormallyJavaScript'sgarbage
collectorwouldcleanupthescopeobjectcreatedformakeAdderatthispoint,butthereturnedfunction
maintainsareferencebacktothatscopeobject.Asaresult,thescopeobjectwillnotbegarbagecollecteduntiltherearenomorereferencestothefunctionobjectthatmakeAdderreturned.
Scopeobjectsformachaincalledthescopechain,similartotheprototypechainusedbyJavaScript'sobjectsystem.
Aclosureisthecombinationofafunctionandthescopeobjectinwhichitwascreated.
Closuresletyousavestateassuch,theycanoftenbeusedinplaceofobjects.Severalexcellentintroductionstoclosurescanbefound here.
MemoryleaksAnunfortunatesideeffectofclosuresisthattheymakeittriviallyeasytoleakmemoryinInternetExplorer.JavaScriptisagarbagecollectedlanguageobjectsareallocatedmemoryupontheircreationandthatmemoryisreclaimedbythebrowserwhennoreferencestoanobjectremain.Objectsprovidedbythehostenvironmentarehandledbythatenvironment.
x(6);//returns11y(7);//returns27
1
2
-
BrowserhostsneedtomanagealargenumberofobjectsrepresentingtheHTMLpagebeingpresentedtheobjectsoftheDOM.Itisuptothebrowsertomanagetheallocationandrecoveryofthese.
InternetExplorerusesitsowngarbagecollectionschemeforthis,separatefromthemechanismusedforJavaScript.Itistheinteractionbetweenthetwothatcancausememoryleaks.
AmemoryleakinIEoccursanytimeacircularreferenceisformedbetweenaJavaScriptobjectandanativeobject.Considerthefollowing:
ThecircularreferenceformedabovecreatesamemoryleakIEwillnotfreethememoryusedbyelando
untilthebrowseriscompletelyrestarted.
Theabovecaseislikelytogounnoticedmemoryleaksonlybecomearealconcerninlongrunningapplicationsorapplicationsthatleaklargeamountsofmemoryduetolargedatastructuresorleakpatternswithinloops.
Leaksarerarelythisobviousoftentheleakeddatastructurecanhavemanylayersofreferences,obscuringthecircularreference.
Closuresmakeiteasytocreateamemoryleakwithoutmeaningto.Considerthis:
Theabovecodesetsuptheelementtoturnredwhenitisclicked.Italsocreatesamemoryleak.Why?Becausethereferencetoelisinadvertentlycaughtintheclosurecreatedfortheanonymousinnerfunction.
ThiscreatesacircularreferencebetweenaJavaScriptobject(thefunction)andanativeobject(el).
Thereareanumberofworkaroundsforthisproblem.Thesimplestisnottousetheelvariable:
functionleakMemory(){
varel=document.getElementById('el');
varo={'el':el};
el.o=o;
}
1
2
3
4
5
functionaddHandler(){
varel=document.getElementById('el');
el.onclick=function(){
el.style.backgroundColor='red';
};
}
1
2
3
4
5
6
-
Surprisingly,onetrickforbreakingcircularreferencesintroducedbyaclosureistoaddanotherclosure:
Theinnerfunctionisexecutedstraightaway,andhidesitscontentsfromtheclosurecreatedwithclickHandler.
Anothergoodtrickforavoidingclosuresisbreakingcircularreferencesduringthewindow.onunloadevent.Manyeventlibrarieswilldothisforyou.NotethatdoingsodisablesthebackforwardcacheinFirefox,soyoushouldnotregisteranunloadlistenerinFirefox,unlessyouhaveotherreasonstodoso.
functionaddHandler(){
document.getElementById('el').onclick=function(){
this.style.backgroundColor='red';
};
}
1
2
3
4
5
functionaddHandler(){
varclickHandler=function(){
this.style.backgroundColor='red';
};
(function(){
varel=document.getElementById('el');
el.onclick=clickHandler;
})();
}
1
2
3
4
5
6
7
8
9