a re-introduction to javascript (js tutorial) - javascript | mdn

Upload: bmkmanoj

Post on 06-Mar-2016

45 views

Category:

Documents


0 download

DESCRIPTION

MDN documentation

TRANSCRIPT

  • 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