Tải bản đầy đủ - 0 (trang)
Chapter 9. Classes, Constructors, and Prototypes

Chapter 9. Classes, Constructors, and Prototypes

Tải bản đầy đủ - 0trang

9.1.Constructors

Chapter7showedyouhowtocreateanew,emptyobjecteither

withtheobjectliteral{}orwiththefollowingexpression:

newObject()



YouhavealsoseenotherkindsofJavaScriptobjectscreated

withasimilarsyntax:

vararray=newArray(10);

vartoday=newDate();



Thenewoperatormustbefollowedbyafunctioninvocation.It

createsanewobject,withnopropertiesandtheninvokesthe

function,passingthenewobjectasthevalueofthethis

keyword.Afunctiondesignedtobeusedwiththenewoperatoris

calledaconstructorfunctionorsimplyaconstructor.A

constructor'sjobistoinitializeanewlycreatedobject,setting

anypropertiesthatneedtobesetbeforetheobjectisused.

Youcandefineyourownconstructorfunction,simplybywriting

afunctionthataddspropertiestothis.Thefollowingcode

definesaconstructorandtheninvokesittwicewiththenew

operatortocreatetwonewobjects:

//Definetheconstructor.

//Notehowitinitializestheobjectreferredtoby"this".

functionRectangle(w,h){

this.width=w;

this.height=h;

//Note:noreturnstatementhere

}



//InvoketheconstructortocreatetwoRectangleobjects.

//Wepassthewidthandheighttotheconstructor

//sothatitcaninitializeeachnewobjectappropriately.

varrect1=newRectangle(2,4);//rect1={width:2,heigh

varrect2=newRectangle(8.5,11);//rect2={width:8.5,hei



Noticehowtheconstructorusesitsargumentstoinitialize

propertiesoftheobjectreferredtobythethiskeyword.You

havedefinedaclassofobjectssimplybydefiningan

appropriateconstructorfunction;allobjectscreatedwiththe

Rectangle()constructorarenowguaranteedtohaveinitialized

widthandheightproperties.Thismeansthatyoucanwrite

programsthatrelyonthisfactandtreatallRectangleobjects

uniformly.Becauseeveryconstructordefinesaclassofobjects,

itisstylisticallyimportanttogiveaconstructorfunctionaname

thatindicatestheclassofobjectsitcreates.Creatinga

rectanglewithnewRectangle(1,2)isalotmoreintuitivethanwith

newinit_rect(1,2),forexample.

Constructorfunctionstypicallydonothavereturnvalues.They

initializetheobjectpassedasthevalueofthisandreturn

nothing.However,aconstructorisallowedtoreturnanobject

value,and,ifitdoesso,thatreturnedobjectbecomesthevalue

ofthenewexpression.Inthiscase,theobjectthatwasthevalue

ofthisissimplydiscarded.



9.2.PrototypesandInheritance

RecallfromChapter8thatamethodisafunctionthatis

invokedasapropertyofanobject.Whenafunctionisinvoked

inthisway,theobjectthroughwhichitisaccessedbecomesthe

valueofthethiskeyword.Supposeyou'dliketocomputethe

areaoftherectanglerepresentedbyaRectangleobject.Hereis

onewaytodoit:



functioncomputeAreaOfRectangle(r){returnr.width*r.height;



Thisworks,butitisnotobject-oriented.Whenusingobjects,it

isbettertoinvokeamethodontheobjectratherthanpassing

theobjecttoafunction.Here'showtodothat:

//CreateaRectangleobject

varr=newRectangle(8.5,11);

//Addamethodtoit

r.area=function(){returnthis.width*this.height;}

//Nowinvokethemethodtocomputethearea

vara=r.area();



Havingtoaddamethodtoanobjectbeforeyoucaninvokeitis

silly,ofcourse.Youcanimprovethesituationbyinitializingthe

areapropertytorefertotheareacomputingfunctioninthe

constructor.HereisanimprovedRectangle()constructor:



functionRectangle(w,h){

this.width=w;

this.height=h;

this.area=function(){returnthis.width*this.height;

}



Withthisnewversionoftheconstructor,youcanwritecodelike

this:

//HowbigisasheetofU.S.Letterpaperinsquareinches?

varr=newRectangle(8.5,11);

vara=r.area();



Thissolutionworksbetterbutisstillnotoptimal.Every

rectanglecreatedwillhavethreeproperties.Thewidthandheight

propertiesmaybedifferentforeachrectangle,buttheareaof

everysingleRectangleobjectalwaysreferstothesame

function(someonemightchangeit,ofcourse,butyouusually

intendthemethodsofanobjecttobeconstant).Itisinefficient

touseregularpropertiesformethodsthatareintendedtobe

sharedbyallobjectsofthesameclass(thatis,allobjects

createdwiththesameconstructor).

Thereisasolution,however.ItturnsoutthateveryJavaScript

objectincludesaninternalreferencetoanotherobject,known

asitsprototypeobject.Anypropertiesoftheprototypeappear

tobepropertiesofanobjectforwhichitistheprototype.

AnotherwayofsayingthisisthataJavaScriptobjectinherits

propertiesfromitsprototype.

Intheprevioussection,Ishowedthatthenewoperatorcreatesa

new,emptyobjectandtheninvokesaconstructorfunctionasa

methodofthatobject.Thisisnotthecompletestory,however.

Aftercreatingtheemptyobject,newsetstheprototypeofthat

object.Theprototypeofanobjectisthevalueoftheprototype

propertyofitsconstructorfunction.Allfunctionshavea

prototypepropertythatisautomaticallycreatedandinitialized

whenthefunctionisdefined.Theinitialvalueoftheprototype

propertyisanobjectwithasingleproperty.Thispropertyis



namedconstructorandrefersbacktotheconstructorfunction

withwhichtheprototypeisassociated.(Youmayrecallthe

constructorpropertyfromChapter7;thisiswhyeveryobject

hasaconstructorproperty.)Anypropertiesyouaddtothis

prototypeobjectwillappeartobepropertiesofobjects

initializedbytheconstructor.

Thisisclearerwithanexample.HereagainistheRectangle()

constructor:

//Theconstructorfunctioninitializesthosepropertiesthat

//willbedifferentforeachinstance.

functionRectangle(w,h){

this.width=w;

this.height=h;

}



//Theprototypeobjectholdsmethodsandotherpropertiesthat

//shouldbesharedbyeachinstance.

Rectangle.prototype.area=function(){returnthis.width*th



Aconstructorprovidesanamefora"class"ofobjectsand

initializesproperties,suchaswidthandheight,thatmaybe

differentforeachinstanceoftheclass.Theprototypeobjectis

associatedwiththeconstructor,andeachobjectinitializedby

theconstructorinheritsexactlythesamesetofpropertiesfrom

theprototype.Thismeansthattheprototypeobjectisanideal

placeformethodsandotherconstantproperties.

Notethatinheritanceoccursautomaticallyaspartofthe

processoflookingupapropertyvalue.Propertiesarenot

copiedfromtheprototypeobjectintonewobjects;theymerely

appearasiftheywerepropertiesofthoseobjects.Thishastwo

importantimplications.First,theuseofprototypeobjectscan

dramaticallydecreasetheamountofmemoryrequiredbyeach



objectbecausetheobjectcaninheritmanyofitsproperties.

Thesecondimplicationisthatanobjectinheritspropertieseven

iftheyareaddedtoitsprototypeaftertheobjectiscreated.

Thismeansthatitispossible(thoughnotnecessarilyagood

idea)toaddnewmethodstoexistingclasses.

Inheritedpropertiesbehavejustlikeregularpropertiesofan

object.Theyareenumeratedbythefor/inloopandcanbe

testedwiththeinoperator.Youcandistinguishthemonlywith

theObject.hasOwnProperty()method:



varr=newRectangle(2,3);

r.hasOwnProperty("width");//true:widthisadirectpropert

r.hasOwnProperty("area");//false:areaisaninheritedpro

"area"inr;//true:"area"isapropertyofr



9.2.1.ReadingandWritingInheritedProperties

Eachclasshasoneprototypeobject,withonesetofproperties.

Buttherearepotentiallymanyinstancesofaclass,eachof

whichinheritsthoseprototypeproperties.Becauseone

prototypepropertycanbeinheritedbymanyobjects,JavaScript

mustenforceafundamentalasymmetrybetweenreadingand

writingpropertyvalues.Whenyoureadpropertypofanobject

o,JavaScriptfirstcheckstoseeifohasapropertynamedp.Ifit

doesnot,itnextcheckstoseeiftheprototypeobjectofohasa

propertynamedp.Thisiswhatmakesprototype-based

inheritancework.

Whenyouwritethevalueofaproperty,ontheotherhand,

JavaScriptdoesnotusetheprototypeobject.Toseewhy,

considerwhatwouldhappenifitdid:supposeyoutrytosetthe

valueofthepropertyo.pwhentheobjectodoesnothavea

propertynamedp.FurthersupposethatJavaScriptgoesahead



andlooksupthepropertypintheprototypeobjectofoand

allowsyoutosetthepropertyoftheprototype.Nowyouhave

changedthevalueofpforawholeclassofobjectsnotatall

whatyouintended.

Therefore,propertyinheritanceoccursonlywhenyouread

propertyvalues,notwhenyouwritethem.Ifyousetthe

propertypinanobjectothatinheritsthatpropertyfromits

prototype,whathappensisthatyoucreateanewpropertyp

directlyino.Nowthatohasitsownpropertynamedp,itno

longerinheritsthevalueofpfromitsprototype.Whenyouread

thevalueofp,JavaScriptfirstlooksatthepropertiesofo.Since

itfindspdefinedino,itdoesn'tneedtosearchtheprototype

objectandneverfindsthevalueofpdefinedthere.We

sometimessaythatthepropertypino"shadows"or"hides"the

propertypintheprototypeobject.Prototypeinheritancecanbe

aconfusingtopic.Figure9-1illustratestheconceptsdiscussed

here.



Figure9-1.Objectsandprototypes



Becauseprototypepropertiesaresharedbyallobjectsofa

class,itgenerallymakessensetousethemtodefineonly

propertiesthatarethesameforallobjectswithintheclass.

Thismakesprototypesidealfordefiningmethods.Other

propertieswithconstantvalues(suchasmathematical

constants)arealsosuitablefordefinitionwithprototype

properties.Ifyourclassdefinesapropertywithavery

commonlyuseddefaultvalue,youmightdefinethisproperty

anditsdefaultvalueinaprototypeobject.Then,thefew

objectsthatwanttodeviatefromthedefaultvaluecancreate

theirownprivate,unsharedcopiesofthepropertyanddefine

theirownnondefaultvalues.



9.2.2.ExtendingBuilt-inTypes

Itisnotonlyuser-definedclassesthathaveprototypeobjects.

Built-inclasses,suchasStringandDate,haveprototype

objectstoo,andyoucanassignvaluestothem.Forexample,

thefollowingcodedefinesanewmethodthatisavailableforall

Stringobjects:

//Returnstrueifthelastcharacterisc

String.prototype.endsWith=function(c){

return(c==this.charAt(this.length-1))

}



HavingdefinedthenewendsWith()methodintheString

prototypeobject,youcanuseitlikethis:

varmessage="helloworld";

message.endsWith('h')//Returnsfalse

message.endsWith('d')//Returnstrue



Thereisastrongargumentagainstextendingbuilt-intypeswith

yourownmethods;ifyoudoso,youareessentiallycreating

yourowncustomversionofthecoreJavaScriptAPI.Anyother

programmerswhohavetoreadormaintainyourcodewilllikely

finditconfusingifyourcodeincludesmethodstheyhavenever

heardof.Unlessyouarecreatingalow-levelJavaScript

frameworkthatyouexpecttobeadoptedbymanyother

programmers,itisprobablybesttostayawayfromthe

prototypeobjectsofthebuilt-intypes.

NotethatyoumustneveraddpropertiestoObject.prototype.Any

propertiesormethodsyouaddareenumerablewithafor/in



loop,andaddingthemtoObject.prototypemakesthemvisiblein

everysingleJavaScriptobject.Anemptyobject,{},isexpected

tohavenoenumerableproperties.Anythingaddedto

Object.prototypebecomesanenumerablepropertyoftheempty

objectandwilllikelybreakcodethatusesobjectsasassociative

arrays.

Thetechniqueshownhereforextendingbuilt-inobjecttypesis

guaranteedtoworkonlyforcoreJavaScript"nativeobjects."

WhenJavaScriptisembeddedinsomecontext,suchasaweb

browseroraJavaapplication,ithasaccesstoadditional"host

objects"suchasobjectsthatrepresentwebbrowserdocument

content.Thesehostobjectsdonottypicallyhaveconstructors

andprototypeobjects,andyouusuallycannotextendthem.

Thereisonecaseinwhichitissafeandusefultoextendthe

prototypeofabuilt-innativeclass:toaddstandardmethodsto

aprototypewhenanoldorincompatibleJavaScript

implementationlacksthem.Forexample,theFunction.apply()

methodismissinginMicrosoftInternetExplorer4and5.Thisis

aprettyimportantfunction,andyoumayseecodelikethisto

replaceit:



//IE4&5don'timplementFunction.apply().

//ThisworkaroundisbasedoncodebyAaronBoodman.

if(!Function.prototype.apply){

//Invokethisfunctionasamethodofthespecifiedobject

//passingthespecifiedparameters.Wehavetouseeval(

Function.prototype.apply=function(object,parameters){

varf=this;//Thefunctiontoinvoke

varo=object||window;//Theobjecttoinvokeit

varargs=parameters||[];//Theargumentstopass



//Temporarilymakethefunctionintoamethodofo

//Todothisweuseapropertynamethatisunlikelyt

o._$_apply_$_=f;



//Wewilluseeval()toinvokethemethod.Todothis



//towritetheinvocationasastring.Firstbuildthe

varstringArgs=[];

for(vari=0;i
stringArgs[i]="args["+i+"]";



//Concatenatetheargumentstringsintoacomma-separa

vararglist=stringArgs.join(",");

//Nowbuildtheentiremethodcallstring

varmethodcall="o._$_apply_$_("+arglist+");";

//Usetheeval()functiontomakethemethodcall

varresult=eval(methodcall);

//Unbindthefunctionfromtheobject

deleteo._$_apply_$_;

//Andreturntheresult

returnresult;

};

}



Asanotherexample,considerthenewarraymethods

implementedinFirefox1.5(seeSection7.7.10.).Ifyouwantto

usethenewArray.map()methodbutalsowantyourcodeto

workonplatformsthatdonotsupportthismethodnatively,you

canusethiscodeforcompatibility:



//Array.map()invokesafunctionfoneachelementofthearr

//returninganewarrayofthevaluesthatresultfromeachfu

//call.Ifmap()iscalledwithtwoarguments,thefunctionf

//isinvokedasamethodofthesecondargument.Wheninvoked,

//ispassed3arguments.Thefirstisthevalueofthearray

//element.Thesecondistheindexofthearrayelement,andt

//thirdisthearrayitself.Inmostcasesitneedstouseonl



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 9. Classes, Constructors, and Prototypes

Tải bản đầy đủ ngay(0 tr)

×