Anda di halaman 1dari 26

A re-introduction to JavaScript (JS

tutorial)
by109contributors:

Showall

Introduction
Whyareintroduction?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.

Overview
JavaScriptisanobjectorienteddynamiclanguagewithtypesandoperators,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.

Numbers
NumbersinJavaScriptare"doubleprecision64bitformatIEEE754values",accordingtothespec.Thishas
someinterestingconsequences.There'snosuchthingasanintegerinJavaScript,soyouhavetobealittle
carefulwithyourarithmeticifyou'reusedtomathinCorJava.Watchoutforstufflike:

0.1+0.2==0.30000000000000004

Inpractice,integervaluesaretreatedas32bitints(andarestoredthatwayinsomebrowser
implementations),whichcanbeimportantforbitwiseoperations.
Thestandardarithmeticoperatorsaresupported,includingaddition,subtraction,modulus(orremainder)
arithmeticandsoforth.There'salsoabuiltinobjectthatIforgottomentionearliercalledMathifyouwantto
performmoreadvancedmathematicalfunctionsandconstants:

1
2

Math.sin(3.5);
varcircumference=Math.PI*(r+r);

YoucanconvertastringtoanintegerusingthebuiltinparseInt()function.Thistakesthebaseforthe
conversionasanoptionalsecondargument,whichyoushouldalwaysprovide:

1
2

parseInt("123",10);//123
parseInt("010",10);//10

Ifyoudon'tprovidethebase,youcangetsurprisingresultsinolderbrowsers(pre2013):

parseInt("010");//8

ThathappenedbecausetheparseInt()functiondecidedtotreatthestringasoctalduetotheleading0.
Ifyouwanttoconvertabinarynumbertoaninteger,justchangethebase:

parseInt("11",2);//3

Similarly,youcanparsefloatingpointnumbersusingthebuiltinparseFloat()functionwhichusesbase10
alwaysunlikeitsparseInt()cousin.
Youcanalsousetheunary+operatortoconvertvaluestonumbers:

+"42";//42

AspecialvaluecalledNaN(shortfor"NotaNumber")isreturnedifthestringisnonnumeric:

parseInt("hello",10);//NaN

NaNistoxic:ifyouprovideitasaninputtoanymathematicaloperationtheresultwillalsobeNaN:

NaN+5;//NaN

YoucantestforNaNusingthebuiltinisNaN()function:

isNaN(NaN);//true

JavaScriptalsohasthespecialvaluesInfinityandInfinity:

1
2

1/0;//Infinity
1/0;//Infinity

YoucantestforInfinity,InfinityandNaNvaluesusingthebuiltinisFinite()function:

1
2
3

isFinite(1/0);//false
isFinite(Infinity);//false
isFinite(NaN);//false

Note: The parseInt() and parseFloat() functions parse a string until they reach a character that
isn'tvalidforthespecifiednumberformat,thenreturnthenumberparseduptothatpoint.Howeverthe"+"operator

simplyconvertsthestringtoNaNifthereisanyinvalidcharacterinit.Justtryparsingthestring"10.2abc"witheach
methodbyyourselfintheconsoleandyou'llunderstandthedifferencesbetter.

Strings
StringsinJavaScriptaresequencesofcharacters.Moreaccurately,theyaresequencesofUnicode
characters,witheachcharacterrepresentedbya16bitnumber.Thisshouldbewelcomenewstoanyone
whohashadtodealwithinternationalization.
Ifyouwanttorepresentasinglecharacter,youjustuseastringoflength1.
Tofindthelengthofastring,accessitslengthproperty:

"hello".length;//5

There'sourfirstbrushwithJavaScriptobjects!DidImentionthatyoucanusestringslikeobjectstoo?They
havemethodsaswellthatallowyoutomanipulatethestringandaccessinformationaboutthestring:

1
2
3

"hello".charAt(0);//"h"
"hello,world".replace("hello","goodbye");//"goodbye,world"
"hello".toUpperCase();//"HELLO"

Othertypes
JavaScriptdistinguishesbetweennull,whichisavaluethatindicatesadeliberatenonvalue(andisonly
accessiblethroughthenullkeyword),andundefined,whichisavalueoftype'undefined'thatindicatesan
uninitializedvaluethatis,avaluehasn'tevenbeenassignedyet.We'lltalkaboutvariableslater,butin
JavaScriptitispossibletodeclareavariablewithoutassigningavaluetoit.Ifyoudothis,thevariable'stype
isundefined.undefinedisactuallyaconstant.
JavaScripthasabooleantype,withpossiblevaluestrueandfalse(bothofwhicharekeywords).Any
valuecanbeconvertedtoabooleanaccordingtothefollowingrules:
1. false,0,theemptystring(""),NaN,null,andundefinedallbecomefalse.
2. allothervaluesbecometrue.
YoucanperformthisconversionexplicitlyusingtheBoolean()function:

1
2

Boolean("");//false
Boolean(234);//true

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:

1
2

vara;
varname="simon";

Ifyoudeclareavariablewithoutassigninganyvaluetoit,itstypeisundefined.
AnimportantdifferencefromotherlanguageslikeJavaisthatinJavaScript,blocksdonothavescopeonly
functionshavescope.Soifavariableisdefinedusingvarinacompoundstatement(forexampleinsidean
ifcontrolstructure),itwillbevisibletotheentirefunction.However,startingwithECMAScriptEdition6,let
andconstdeclarationsallowyoutocreateblockscopedvariables.

Operators
JavaScript'snumericoperatorsare+,,*,/and%whichistheremainderoperator.Valuesareassigned
using=,andtherearealsocompoundassignmentstatementssuchas+=and=.Theseextendouttox=
xoperatory.

1
2

x+=5
x=x+5

Youcanuse++andtoincrementanddecrementrespectively.Thesecanbeusedasprefixorpostfix
operators.
The+operatoralsodoesstringconcatenation:

"hello"+"world";//"helloworld"

Ifyouaddastringtoanumber(orothervalue)everythingisconvertedintoastringfirst.Thismightcatch
youup:

1
2

"3"+4+5;//"345"
3+4+"5";//"75"

Addinganemptystringtosomethingisausefulwayofconvertingit.
ComparisonsinJavaScriptcanbemadeusing<,>,<=and>=.Theseworkforbothstringsandnumbers.
Equalityisalittlelessstraightforward.Thedoubleequalsoperatorperformstypecoercionifyougiveit
differenttypes,withsometimesinterestingresults:

1
2

"dog"=="dog";//true
1==true;//true

Toavoidtypecoercion,usethetripleequalsoperator:

1
2

1===true;//false
true===true;//true

Therearealso!=and!==operators.
JavaScriptalsohasbitwiseoperations.Ifyouwanttousethem,they'rethere.

Controlstructures
JavaScripthasasimilarsetofcontrolstructurestootherlanguagesintheCfamily.Conditionalstatements
aresupportedbyifandelseyoucanchainthemtogetherifyoulike:

1
2
3
4
5
6

varname="kittens";
if(name=="puppies"){
name+="!";
}elseif(name=="kittens"){
name+="!!";
}else{

7
8
9

name="!"+name;
}
name=="kittens!!"

JavaScripthaswhileloopsanddowhileloops.Thefirstisgoodforbasicloopingthesecondforloops
whereyouwishtoensurethatthebodyoftheloopisexecutedatleastonce:

1
2
3

while(true){
//aninfiniteloop!
}

4
5
6
7
8

varinput;
do{
input=get_input();
}while(inputIsNotValid(input))

JavaScript'sforloopisthesameasthatinCandJava:itletsyouprovidethecontrolinformationforyour
looponasingleline.

1
2
3

for(vari=0;i<5;i++){
//Willexecute5times
}

The&&and||operatorsuseshortcircuitlogic,whichmeanswhethertheywillexecutetheirsecondoperand
isdependentonthefirst.Thisisusefulforcheckingfornullobjectsbeforeaccessingtheirattributes:

varname=o&&o.getName();

Orforsettingdefaultvalues:

varname=otherName||"default";

JavaScripthasaternaryoperatorforconditionalexpressions:

varallowed=(age>18)?"yes":"no";

Theswitchstatementcanbeusedformultiplebranchesbasedonanumberorstring:

1
2
3
4
5
6
7
8
9
10

switch(action){
case'draw':
drawIt();
break;
case'eat':
eatIt();
break;
default:
doNothing();
}

Ifyoudon'taddabreakstatement,executionwill"fallthrough"tothenextlevel.Thisisveryrarelywhatyou
wantinfactit'sworthspecificallylabelingdeliberatefallthroughwithacommentifyoureallymeantitto
aiddebugging:

1
2
3
4
5
6
7
8

switch(a){
case1://fallthrough
case2:
eatIt();
break;
default:
doNothing();
}

Thedefaultclauseisoptional.Youcanhaveexpressionsinboththeswitchpartandthecasesifyoulike
comparisonstakeplacebetweenthetwousingthe===operator:

1
2
3
4
5
6
7

switch(1+3){
case2+2:
yay();
break;
default:
neverhappens();
}

Objects
JavaScriptobjectscanbethoughtofassimplecollectionsofnamevaluepairs.Assuch,theyaresimilarto:

DictionariesinPython
HashesinPerlandRuby
HashtablesinCandC++
HashMapsinJava
AssociativearraysinPHP
Thefactthatthisdatastructureissowidelyusedisatestamenttoitsversatility.Sinceeverything(barcore
types)inJavaScriptisanobject,anyJavaScriptprogramnaturallyinvolvesagreatdealofhashtable
lookups.It'sagoodthingthey'resofast!
The"name"partisaJavaScriptstring,whilethevaluecanbeanyJavaScriptvalueincludingmore
objects.Thisallowsyoutobuilddatastructuresofarbitrarycomplexity.
Therearetwobasicwaystocreateanemptyobject:

varobj=newObject();

And:

varobj={};

Thesearesemanticallyequivalentthesecondiscalledobjectliteralsyntax,andismoreconvenient.This
syntaxisalsothecoreofJSONformatandshouldbepreferredatalltimes.
Objectliteralsyntaxcanbeusedtoinitializeanobjectinitsentirety:

1
2
3
4
5
6
7
8

varobj={
name:"Carrot",
"for":"Max",
details:{
color:"orange",
size:12
}
}

Attributeaccesscanbechainedtogether:

1
2

obj.details.color;//orange
obj["details"]["size"];//12

Thefollowingexamplecreatesanobjectprototype,Person,andinstanceofthatprototype,You.

1
2
3
4

functionPerson(name,age){
this.name=name;
this.age=age;
}

5
6
7
8
9

//Defineanobject
varYou=newPerson("You",24);
//Wearecreatinganewpersonnamed"You"
//(thatwasthefirstparameter,andtheage..)

Oncecreated,anobject'spropertiescanagainbeaccessedinoneoftwoways:

1
2

obj.name="Simon";
varname=obj.name;

And...

1
2

obj["name"]="Simon";
varname=obj["name"];

Thesearealsosemanticallyequivalent.Thesecondmethodhastheadvantagethatthenameofthe
propertyisprovidedasastring,whichmeansitcanbecalculatedatruntimethoughusingthismethod
preventssomeJavaScriptengineandminifieroptimizationsbeingapplied.Itcanalsobeusedtosetandget
propertieswithnamesthatarereservedwords:

1
2

obj.for="Simon";//Syntaxerror,because'for'isareservedword
obj["for"]="Simon";//worksfine

Note:StartingfromEcmaScript5,reservedwordsmaybeusedasobjectpropertynames"inthebuff".
Thismeansthattheydon'tneedtobe"clothed"inquoteswhendefiningobjectliterals.SeeES5
Spec.

Formoreonobjectsandprototypessee:Object.prototype.

Arrays
ArraysinJavaScriptareactuallyaspecialtypeofobject.Theyworkverymuchlikeregularobjects
(numericalpropertiescannaturallybeaccessedonlyusing[]syntax)buttheyhaveonemagicproperty
called'length'.Thisisalwaysonemorethanthehighestindexinthearray.
Onewayofcreatingarraysisasfollows:

1
2
3
4
5

vara=newArray();
a[0]="dog";
a[1]="cat";
a[2]="hen";
a.length;//3

Amoreconvenientnotationistouseanarrayliteral:

1
2

vara=["dog","cat","hen"];
a.length;//3

Notethatarray.lengthisn'tnecessarilythenumberofitemsinthearray.Considerthefollowing:

1
2
3

vara=["dog","cat","hen"];
a[100]="fox";
a.length;//101

Rememberthelengthofthearrayisonemorethanthehighestindex.
Ifyouqueryanonexistentarrayindex,yougetundefined:

typeofa[90];//undefined

Ifyoutaketheaboveintoaccount,youcaniterateoveranarrayusingthefollowing:

1
2

for(vari=0;i<a.length;i++){
//Dosomethingwitha[i]

Thisisslightlyinefficientasyouarelookingupthelengthpropertyonceeveryloop.Animprovementisthis:

1
2
3

for(vari=0,len=a.length;i<len;i++){
//Dosomethingwitha[i]
}

Anicerlookingbutlimitedidiomis:

1
2
3

for(vari=0,item;item=a[i++];){
//Dosomethingwithitem
}

Herewearesettinguptwovariables.Theassignmentinthemiddlepartoftheforloopisalsotestedfor
truthfulnessifitsucceeds,theloopcontinues.Sinceiisincrementedeachtime,itemsfromthearraywill
beassignedtoiteminsequentialorder.Theloopstopswhena"falsy"itemisfound(suchasundefined).
Thistrickshouldonlybeusedforarrayswhichyouknowdonotcontain"falsy"values(arraysofobjectsor
DOMnodesforexample).Ifyouareiteratingovernumericdatathatmightincludea0orstringdatathat
mightincludetheemptystringyoushouldusethei,lenidiominstead.
Youcaniterateoveranarrayusingafor...inloop.Notethatifsomeoneaddednewpropertiesto
Array.prototype,theywillalsobeiteratedoverbythisloop.Thereforethismethodis"not"recommended.
AnotherwayofiteratingoveranarraythatwasaddedwithECMAScript5isforEach():

1
2
3

["dog","cat","hen"].forEach(function(currentValue,index,array){
//DosomethingwithcurrentValueorarray[index]
});

Ifyouwanttoappendanitemtoanarraysimplydoitlikethis:

a.push(item);

Arrayscomewithanumberofmethods.Seealsothefulldocumentationforarraymethods.

Methodname
a.toString()

a.toLocaleString()
a.concat(item1[,item2[,...[,
itemN]]])
a.join(sep)

Description
ReturnsastringwiththetoString()ofeachelement
separatedbycommas.
ReturnsastringwiththetoLocaleString()ofeach
elementseparatedbycommas.
Returnsanewarraywiththeitemsaddedontoit.
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[,

Letsyoumodifyanarraybydeletingasectionand

...[,itemN]]])

replacingitwithmoreitems.

a.unshift([item])

Prependsitemstothestartofthearray.

Functions
Alongwithobjects,functionsarethecorecomponentinunderstandingJavaScript.Themostbasicfunction
couldn'tbemuchsimpler:

1
2
3
4

functionadd(x,y){
vartotal=x+y;
returntotal;
}

Thisdemonstratesabasicfunction.AJavaScriptfunctioncantake0ormorenamedparameters.The
functionbodycancontainasmanystatementsasyoulike,andcandeclareitsownvariableswhicharelocal
tothatfunction.Thereturnstatementcanbeusedtoreturnavalueatanytime,terminatingthefunction.If

noreturnstatementisused(oranemptyreturnwithnovalue),JavaScriptreturnsundefined.
Thenamedparametersturnouttobemorelikeguidelinesthananythingelse.Youcancallafunction
withoutpassingtheparametersitexpects,inwhichcasetheywillbesettoundefined.

1
2

add();//NaN
//Youcan'tperformadditiononundefined

Youcanalsopassinmoreargumentsthanthefunctionisexpecting:

1
2

add(2,3,4);//5
//addedthefirsttwo;4wasignored

Thatmayseemalittlesilly,butfunctionshaveaccesstoanadditionalvariableinsidetheirbodycalled
arguments,whichisanarraylikeobjectholdingallofthevaluespassedtothefunction.Let'srewritethe
addfunctiontotakeasmanyvaluesaswewant:

1
2
3
4
5
6
7

functionadd(){
varsum=0;
for(vari=0,j=arguments.length;i<j;i++){
sum+=arguments[i];
}
returnsum;
}

8
9

add(2,3,4,5);//14

That'sreallynotanymoreusefulthanwriting2+3+4+5though.Let'screateanaveragingfunction:

1
2
3
4
5
6
7

functionavg(){
varsum=0;
for(vari=0,j=arguments.length;i<j;i++){
sum+=arguments[i];
}
returnsum/arguments.length;
}

8
9

avg(2,3,4,5);//3.5

Thisisprettyuseful,butintroducesanewproblem.Theavg()functiontakesacommaseparatedlistof
argumentsbutwhatifyouwanttofindtheaverageofanarray?Youcouldjustrewritethefunctionas
follows:

1
2
3
4
5
6
7

functionavgArray(arr){
varsum=0;
for(vari=0,j=arr.length;i<j;i++){
sum+=arr[i];
}
returnsum/arr.length;
}

8
9

avgArray([2,3,4,5]);//3.5

Butitwouldbenicetobeabletoreusethefunctionthatwe'vealreadycreated.Luckily,JavaScriptletsyou
callafunctionandcallitwithanarbitraryarrayofarguments,usingtheapply()methodofanyfunction
object.

avg.apply(null,[2,3,4,5]);//3.5

Thesecondargumenttoapply()isthearraytouseasargumentsthefirstwillbediscussedlateron.This
emphasizesthefactthatfunctionsareobjectstoo.
JavaScriptletsyoucreateanonymousfunctions.

1
2
3
4
5
6
7

varavg=function(){
varsum=0;
for(vari=0,j=arguments.length;i<j;i++){
sum+=arguments[i];
}
returnsum/arguments.length;
};

Thisissemanticallyequivalenttothefunctionavg()form.It'sextremelypowerful,asitletsyouputafull
functiondefinitionanywherethatyouwouldnormallyputanexpression.Thisenablesallsortsofclever
tricks.Here'sawayof"hiding"somelocalvariableslikeblockscopeinC:

1
2

vara=1;
varb=2;

3
4
5
6
7

(function(){
varb=3;
a+=b;
})();

8
9
10

a;//4
b;//2

JavaScriptallowsyoutocallfunctionsrecursively.Thisisparticularlyusefulfordealingwithtreestructures,
suchasyougetinthebrowserDOM.

1
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;
}

Thishighlightsapotentialproblemwithanonymousfunctions:howdoyoucallthemrecursivelyiftheydon't
haveaname?JavaScriptletsyounamefunctionexpressionsforthis.YoucanusenamedIIFEs
(ImmediatelyInvokedFunctionExpressions)asbelow:

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);

Thenameprovidedtoafunctionexpressionasaboveisonlyavailabletothefunction'sownscope.This
bothallowsmoreoptimizationstobedonebytheengineandamorereadablecode.Thenamealsoshows
upinthedebuggerandsomestacktraceswhichcansaveyoutime.

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:

1
2
3
4
5
6
7
8
9
10
11
12

functionmakePerson(first,last){
return{
first:first,
last:last
};
}
functionpersonFullName(person){
returnperson.first+''+person.last;
}
functionpersonFullNameReversed(person){
returnperson.last+','+person.first;
}

13
14
15
16

s=makePerson("Simon","Willison");
personFullName(s);//"SimonWillison"
personFullNameReversed(s);"Willison,Simon"

Thisworks,butit'sprettyugly.Youendupwithdozensoffunctionsinyourglobalnamespace.Whatwe
reallyneedisawaytoattachafunctiontoanobject.Sincefunctionsareobjects,thisiseasy:

1
2
3
4
5
6
7

functionmakePerson(first,last){
return{
first:first,
last:last,
fullName:function(){
returnthis.first+''+this.last;
},

8
9
10
11
12

fullNameReversed:function(){
returnthis.last+','+this.first;
}
};
}

13
14
15
16

s=makePerson("Simon","Willison")
s.fullName();//"SimonWillison"
s.fullNameReversed();//"Willison,Simon"

There'ssomethingherewehaven'tseenbefore:thethiskeyword.Usedinsideafunction,thisrefersto
thecurrentobject.Whatthatactuallymeansisspecifiedbythewayinwhichyoucalledthatfunction.Ifyou
calleditusingdotnotationorbracketnotationonanobject,thatobjectbecomesthis.Ifdotnotationwasn't
usedforthecall,thisreferstotheglobalobject.
Notethatthisisafrequentcauseofmistakes.Forexample:

1
2
3

s=makePerson("Simon","Willison");
varfullName=s.fullName;
fullName();//undefinedundefined

WhenwecallfullName()alone,withoutusings.fullName(),thisisboundtotheglobalobject.Since
therearenoglobalvariablescalledfirstorlastwegetundefinedforeachone.
WecantakeadvantageofthethiskeywordtoimproveourmakePersonfunction:

1
2
3
4
5
6
7
8
9
10
11

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");

Wehaveintroducedanotherkeyword:new.newisstronglyrelatedtothis.Whatitdoesisitcreatesabrand
newemptyobject,andthencallsthefunctionspecified,withthissettothatnewobject.Noticethoughthat

thefunctionspecifiedwiththisdoesnotreturnavaluebutmerelymodifiesthethisobject.It'snewthat
returnsthethisobjecttothecallingsite.Functionsthataredesignedtobecalledbynewarecalled
constructorfunctions.Commonpracticeistocapitalizethesefunctionsasaremindertocallthemwithnew.
TheimprovedfunctionstillhasthesamepitfallwithcallingfullName()alone.
Ourpersonobjectsaregettingbetter,buttherearestillsomeuglyedgestothem.Everytimewecreatea
personobjectwearecreatingtwobrandnewfunctionobjectswithinitwouldn'titbebetterifthiscode
wasshared?

1
2
3
4
5
6
7
8
9
10
11
12

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;
}

That'sbetter:wearecreatingthemethodfunctionsonlyonce,andassigningreferencestotheminsidethe
constructor.Canwedoanybetterthanthat?Theanswerisyes:

1
2
3
4
5
6
7
8
9
10

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;
};

Person.prototypeisanobjectsharedbyallinstancesofPerson.Itformspartofalookupchain(thathasa
specialname,"prototypechain"):anytimeyouattempttoaccessapropertyofPersonthatisn'tset,
JavaScriptwillcheckPerson.prototypetoseeifthatpropertyexiststhereinstead.Asaresult,anything
assignedtoPerson.prototypebecomesavailabletoallinstancesofthatconstructorviathethisobject.

Thisisanincrediblypowerfultool.JavaScriptletsyoumodifysomething'sprototypeatanytimeinyour
program,whichmeansyoucanaddextramethodstoexistingobjectsatruntime:

1
2

s=newPerson("Simon","Willison");
s.firstNameCaps();//TypeErroronline1:s.firstNameCapsisnotafunction

3
4
5
6
7

Person.prototype.firstNameCaps=function(){
returnthis.first.toUpperCase()
};
s.firstNameCaps();//"SIMON"

Interestingly,youcanalsoaddthingstotheprototypeofbuiltinJavaScriptobjects.Let'saddamethodto
Stringthatreturnsthatstringinreverse:

1
2

vars="Simon";
s.reversed();//TypeErroronline1:s.reversedisnotafunction

3
4
5
6
7
8
9
10

String.prototype.reversed=function(){
varr="";
for(vari=this.length1;i>=0;i){
r+=this[i];
}
returnr;
};

11
12

s.reversed();//nomiS

Ournewmethodevenworksonstringliterals!

"Thiscannowbereversed".reversed();//desreverebwonnacsihT

AsImentionedbefore,theprototypeformspartofachain.TherootofthatchainisObject.prototype,
whosemethodsincludetoString()itisthismethodthatiscalledwhenyoutrytorepresentanobjectas
astring.ThisisusefulfordebuggingourPersonobjects:

1
2

vars=newPerson("Simon","Willison");
s;//[objectObject]

3
4
5

Person.prototype.toString=function(){
return'<Person:'+this.fullName()+'>';

7
8

s.toString();//"<Person:SimonWillison>"

Rememberhowavg.apply()hadanullfirstargument?Wecanrevisitthatnow.Thefirstargumentto
apply()istheobjectthatshouldbetreatedas'this'.Forexample,here'satrivialimplementationofnew:

1
2
3
4
5

functiontrivialNew(constructor,...args){
varo={};//Createanobject
constructor.apply(o,args);
returno;
}

Thisisn'tanexactreplicaofnewasitdoesn'tsetuptheprototypechain(itwouldbedifficulttoillustrate).
Thisisnotsomethingyouuseveryoften,butit'susefultoknowabout.Inthissnippet,...args(including
theellipsis)iscalledthe"restarguments"asthenameimplies,thiscontainstherestofthearguments.
Calling

varbill=trivialNew(Person,"William","Orange");

isthereforealmostequivalentto

varbill=newPerson("William","Orange");

apply()hasasisterfunctionnamedcall,whichagainletsyousetthisbuttakesanexpandedargument
listasopposedtoanarray.

1
2
3
4
5
6
7
8

functionlastNameCaps(){
returnthis.last.toUpperCase();
}
vars=newPerson("Simon","Willison");
lastNameCaps.call(s);
//Isthesameas:
s.lastNameCaps=lastNameCaps;
s.lastNameCaps();

Innerfunctions

Innerfunctions
JavaScriptfunctiondeclarationsareallowedinsideotherfunctions.We'veseenthisoncebefore,withan
earliermakePerson()function.AnimportantdetailofnestedfunctionsinJavaScriptisthattheycanaccess
variablesintheirparentfunction'sscope:

1
2
3
4
5
6
7

functionbetterExampleNeeded(){
vara=1;
functiononeMoreThanA(){
returna+1;
}
returnoneMoreThanA();
}

Thisprovidesagreatdealofutilityinwritingmoremaintainablecode.Ifafunctionreliesononeortwoother
functionsthatarenotusefultoanyotherpartofyourcode,youcannestthoseutilityfunctionsinsidethe
functionthatwillbecalledfromelsewhere.Thiskeepsthenumberoffunctionsthatareintheglobalscope
down,whichisalwaysagoodthing.
Thisisalsoagreatcountertothelureofglobalvariables.Whenwritingcomplexcodeitisoftentemptingto
useglobalvariablestosharevaluesbetweenmultiplefunctionswhichleadstocodethatishardto
maintain.Nestedfunctionscansharevariablesintheirparent,soyoucanusethatmechanismtocouple
functionstogetherwhenitmakessensewithoutpollutingyourglobalnamespace'localglobals'ifyoulike.
Thistechniqueshouldbeusedwithcaution,butit'sausefulabilitytohave.

Closures
ThisleadsustooneofthemostpowerfulabstractionsthatJavaScripthastoofferbutalsothemost
potentiallyconfusing.Whatdoesthisdo?

1
2
3
4
5
6
7
8
9

functionmakeAdder(a){
returnfunction(b){
returna+b;
};
}
varx=makeAdder(5);
vary=makeAdder(20);
x(6);//?
y(7);//?

ThenameofthemakeAdderfunctionshouldgiveitaway:itcreatesnew'adder'functions,whichwhencalled

withoneargumentaddittotheargumentthattheywerecreatedwith.
What'shappeninghereisprettymuchthesameaswashappeningwiththeinnerfunctionsearlieron:a
functiondefinedinsideanotherfunctionhasaccesstotheouterfunction'svariables.Theonlydifferencehere
isthattheouterfunctionhasreturned,andhencecommonsensewouldseemtodictatethatitslocal
variablesnolongerexist.Buttheydostillexistotherwisetheadderfunctionswouldbeunabletowork.
What'smore,therearetwodifferent"copies"ofmakeAdder'slocalvariablesoneinwhichais5andone
inwhichais20.Sotheresultofthosefunctioncallsisasfollows:

1
2

x(6);//returns11
y(7);//returns27

Here'swhat'sactuallyhappening.WheneverJavaScriptexecutesafunction,a'scope'objectiscreatedto
holdthelocalvariablescreatedwithinthatfunction.Itisinitialisedwithanyvariablespassedinasfunction
parameters.Thisissimilartotheglobalobjectthatallglobalvariablesandfunctionslivein,butwithacouple
ofimportantdifferences:firstly,abrandnewscopeobjectiscreatedeverytimeafunctionstartsexecuting,
andsecondly,unliketheglobalobject(whichisaccessibleasthisandinbrowsersisaccessibleaswindow)
thesescopeobjectscannotbedirectlyaccessedfromyourJavaScriptcode.Thereisnomechanismfor
iteratingoverthepropertiesofthecurrentscopeobject,forexample.
SowhenmakeAdderiscalled,ascopeobjectiscreatedwithoneproperty:a,whichistheargumentpassed
tothemakeAdderfunction.makeAdderthenreturnsanewlycreatedfunction.NormallyJavaScript'sgarbage
collectorwouldcleanupthescopeobjectcreatedformakeAdderatthispoint,butthereturnedfunction
maintainsareferencebacktothatscopeobject.Asaresult,thescopeobjectwillnotbegarbagecollected
untiltherearenomorereferencestothefunctionobjectthatmakeAdderreturned.
Scopeobjectsformachaincalledthescopechain,similartotheprototypechainusedbyJavaScript'sobject
system.
Aclosureisthecombinationofafunctionandthescopeobjectinwhichitwascreated.
Closuresletyousavestateassuch,theycanoftenbeusedinplaceofobjects.Severalexcellent
introductionstoclosurescanbefound here.

Memoryleaks
AnunfortunatesideeffectofclosuresisthattheymakeittriviallyeasytoleakmemoryinInternetExplorer.
JavaScriptisagarbagecollectedlanguageobjectsareallocatedmemoryupontheircreationandthat
memoryisreclaimedbythebrowserwhennoreferencestoanobjectremain.Objectsprovidedbythehost
environmentarehandledbythatenvironment.

BrowserhostsneedtomanagealargenumberofobjectsrepresentingtheHTMLpagebeingpresented
theobjectsoftheDOM.Itisuptothebrowsertomanagetheallocationandrecoveryofthese.
InternetExplorerusesitsowngarbagecollectionschemeforthis,separatefromthemechanismusedfor
JavaScript.Itistheinteractionbetweenthetwothatcancausememoryleaks.
AmemoryleakinIEoccursanytimeacircularreferenceisformedbetweenaJavaScriptobjectandanative
object.Considerthefollowing:

1
2
3
4
5

functionleakMemory(){
varel=document.getElementById('el');
varo={'el':el};
el.o=o;
}

ThecircularreferenceformedabovecreatesamemoryleakIEwillnotfreethememoryusedbyelando
untilthebrowseriscompletelyrestarted.
Theabovecaseislikelytogounnoticedmemoryleaksonlybecomearealconcerninlongrunning
applicationsorapplicationsthatleaklargeamountsofmemoryduetolargedatastructuresorleakpatterns
withinloops.
Leaksarerarelythisobviousoftentheleakeddatastructurecanhavemanylayersofreferences,
obscuringthecircularreference.
Closuresmakeiteasytocreateamemoryleakwithoutmeaningto.Considerthis:

1
2
3
4
5
6

functionaddHandler(){
varel=document.getElementById('el');
el.onclick=function(){
el.style.backgroundColor='red';
};
}

Theabovecodesetsuptheelementtoturnredwhenitisclicked.Italsocreatesamemoryleak.Why?
Becausethereferencetoelisinadvertentlycaughtintheclosurecreatedfortheanonymousinnerfunction.
ThiscreatesacircularreferencebetweenaJavaScriptobject(thefunction)andanativeobject(el).
Thereareanumberofworkaroundsforthisproblem.Thesimplestisnottousetheelvariable:

1
2
3
4
5

functionaddHandler(){
document.getElementById('el').onclick=function(){
this.style.backgroundColor='red';
};
}

Surprisingly,onetrickforbreakingcircularreferencesintroducedbyaclosureistoaddanotherclosure:

1
2
3
4
5
6
7
8
9

functionaddHandler(){
varclickHandler=function(){
this.style.backgroundColor='red';
};
(function(){
varel=document.getElementById('el');
el.onclick=clickHandler;
})();
}

Theinnerfunctionisexecutedstraightaway,andhidesitscontentsfromtheclosurecreatedwith
clickHandler.
Anothergoodtrickforavoidingclosuresisbreakingcircularreferencesduringthewindow.onunloadevent.
Manyeventlibrarieswilldothisforyou.NotethatdoingsodisablesthebackforwardcacheinFirefox,so
youshouldnotregisteranunloadlistenerinFirefox,unlessyouhaveotherreasonstodoso.

Anda mungkin juga menyukai