您的位置:首页 > Web前端 > JavaScript

7个相见恨晚的Javascript技巧

2010-08-21 01:21 92 查看
SevenJavaScriptThingsIWishIKnewMuchEarlierInMyCareer

http://www.iwms.net/trans.aspx?id=2217

I’vebeenwritingJavaScriptcodeformuchlongerthanIcaretoremember.Iamveryexcitedaboutthelanguage’srecentsuccess;it’sgoodtobeapartofthatsuccessstory.I’vewrittendozensofarticles,bookchaptersandonefullbookonthematter,andyetIkeepfindingnewthings.Herearesomeofthe“aha!”momentsI’vehadinthepast,whichyoucantryoutratherthanwaitingforthemtocometoyoubychance.

[Offtopic:bytheway,didyouknowthatthereisaSmashingeBookSeries?Book#2isSuccessfulFreelancingforWebDesigners,260pagesforjust$9,90.]

ShortcutNotations

OneofthethingsIlovemostaboutJavaScriptnowisshortcutnotationstogenerateobjectsandarrays.So,inthepastwhenwewantedtocreateanobject,wewrote:

viewsource

print?

1
var
car=
new
Object();
2
car.colour=
'red'
;
3
car.wheels=4;
4
car.hubcaps=
'spinning'
;
5
car.age=4;
Thesamecanbeachievedwith:

viewsource

print?

1
var
car={
2
colour:
'red'
,
3
wheels:4,
4
hubcaps:
'spinning'
,
5
age:4
6
}
Muchshorter,andyoudon’tneedtorepeatthenameoftheobject.Rightnow,
car
isfine,butwhathappenswhenyouuse
invalidUserInSession
?ThemaingotchainthisnotationisIE.Nevereverleaveatrailingcommabeforetheclosingcurlybraceoryou’llbeintrouble.

Theotherhandyshortcutnotationisforarrays.Theoldschoolwayofdefiningarrayswasthis:

viewsource

print?

1
var
moviesThatNeedBetterWriters=
new
Array(
2
'Transformers'
,
'Transformers2'
,
'Avatar'
,
'IndianaJones4'
3
);
Theshorterversionofthisis:

viewsource

print?

1
var
moviesThatNeedBetterWriters=[
2
'Transformers'
,
'Transformers2'
,
'Avatar'
,
'IndianaJones4'
3
];
Theotherthingaboutarraysisthatthereisnosuchthingasanassociativearray.Youwillfindalotofcodeexamplesthatdefinetheabove
car
examplelikeso:

viewsource

print?

1
var
car=
new
Array();
2
car[
'colour'
]=
'red'
;
3
car[
'wheels'
]=4;
4
car[
'hubcaps'
]=
'spinning'
;
5
car[
'age'
]=4;
ThisisnotSparta;thisismadness—don’tbotherwiththis.“Associativearrays”isaconfusingnameforobjects.

Anotherverycoolshortcutnotationistheternarynotationforconditions.So,insteadofthefollowing…

viewsource

print?

1
var
direction;
2
if
(x<200){
3
direction=1;
4
}
else
{
5
direction=-1;
6
}
…Youcouldwriteashorterversionusingtheternarynotation:

viewsource

print?

1
var
direction=x<200?1:-1;
The
true
caseoftheconditionisafterthequestionmark,andtheothercasefollowsthecolon.

JSONAsADataFormat

BeforeIdiscoveredJSONtostoredata,IdidallkindsofcrazythingstoputcontentinaJavaScript-readyformat:arrays,stringswithcontrolcharacterstosplit,andotherabominations.ThecreationofJSONbyDouglasCrockfordchangedallthat.UsingJSON,youcanstorecomplexdatainaformatthatisnativetoJavaScriptanddoesn'tneedanyextraconversiontobeusedimmediately.

JSONisshortfor"JavaScriptObjectNotation"andusesbothoftheshortcutswecoveredearlier.

So,ifIwantedtodescribeaband,forexample,Icoulddothefollowing:

viewsource

print?

01
var
band={
02
"name"
:
"TheRedHotChiliPeppers"
,
03
"members"
:[
04
{
05
"name"
:
"AnthonyKiedis"
,
06
"role"
:
"leadvocals"
07
},
08
{
09
"name"
:
"Michael'Flea'Balzary"
,
10
"role"
:
"bassguitar,trumpet,backingvocals"
11
},
12
{
13
"name"
:
"ChadSmith"
,
14
"role"
:
"drums,percussion"
15
},
16
{
17
"name"
:
"JohnFrusciante"
,
18
"role"
:
"LeadGuitar"
19
}
20
],
21
"year"
:
"2009"
22
}
YoucanuseJSONdirectlyinJavaScriptand,whenwrappedinafunctioncall,evenasareturnformatofAPIs.ThisiscalledJSON-PandissupportedbyalotofAPIsoutthere.Youcanuseadataendpoint,returningJSON-Pdirectlyinascriptnode:

viewsource

print?

01
<divid=
"delicious"
></div><script>
02
function
delicious(o){
03
var
out=
'<ul>'
;
04
for
(
var
i=0;i<o.length;i++){
05
out+=
'<li><ahref="'
+o[i].u+
'">'
+
06
o[i].d+
'</a></li>'
;
07
}
08
out+=
'</ul>'
;
09
document.getElementById(
'delicious'
).innerHTML=out;
10
}
11
</script>
12
<scriptsrc=
"http://feeds.delicious.com/v2/json/codepo8/javascript?count=15&callback=delicious"
></script>
ThiscallstheDeliciousWebservicetogetmylatestJavaScriptbookmarksinJSONformatandthendisplaysthemasanunorderedlist.

Inessence,JSONisprobablythemostlightweightwayofdescribingcomplexdata—anditrunsinabrowser.YoucanevenuseitinPHPusingthe
json_decode()
function.

NativeJavaScriptFunctions(Math,ArrayAndString)

OnethingthatamazedmeishowmucheasiermylifegotonceIreadupthoroughlyonthemathandstringfunctionsofJavaScript.Youcanusethesetoavoidalotofloopingandconditions.Forexample,whenIhadthetaskoffindingthelargestnumberinanarrayofnumbers,Iusedtowritealoop,likeso:

viewsource

print?

1
var
numbers=[3,342,23,22,124];
2
var
max=0;
3
for
(
var
i=0;i<numbers.length;i++){
4
if
(numbers[i]>max){
5
max=numbers[i];
6
}
7
}
8
alert(max);
Thiscanbeachievedwithoutaloop:

viewsource

print?

1
var
numbers=[3,342,23,22,124];
2
numbers.sort(
function
(a,b){
return
b-a});
3
alert(numbers[0]);
Noticethatyoucannotuse
sort()
onanumberarraybecauseitsortslexically.There'sagoodtutorialon
sort()
hereincaseyouneedtoknowmore.

Anotherinterestingmethodis
Math.max()
.Thisonereturnsthelargestnumberfromalistofparameters:

viewsource

print?

1
Math.max(12,123,3,2,433,4);
//returns433
Becausethistestsfornumbersandreturnsthelargestone,youcanuseittotestforbrowsersupportofcertainproperties:

viewsource

print?

1
var
scrollTop=Math.max(
2
doc.documentElement.scrollTop,
3
doc.body.scrollTop
4
);
ThisworksaroundanInternetExplorerproblem.Youcanreadoutthe
scrollTop
ofthecurrentdocument,butdependingonthe
DOCTYPE
ofthedocument,oneortheotherpropertyisassignedthevalue.Whenyouuse
Math.max()
yougettherightnumberbecauseonlyoneofthepropertiesreturnsone;theotherwillbe
undefined
.YoucanreadmoreaboutshorteningJavaScriptwithmathfunctionshere.

Otherverypowerfulfunctionstomanipulatestringsare
split()
and
join()
.ProbablythemostpowerfulexampleofthisiswritingafunctiontoattachCSSclassestoelements.

Thethingis,whenyouaddaclasstoaDOMelement,youwanttoadditeitherasthefirstclassortoalreadyexistingclasseswithaspaceinfrontofit.Whenyouremoveclasses,youalsoneedtoremovethespaces(whichwasmuchmoreimportantinthepastwhensomebrowsersfailedtoapplyclasseswithtrailingspaces).

So,theoriginalfunctionwouldbesomethinglike:

viewsource

print?

1
function
addclass(elm,newclass){
2
var
c=elm.className;
3
elm.className=(c===
''
)?newclass:c+
''
+newclass;
4
}
Youcanautomatethisusingthe
split()
and
join()
methods:

viewsource

print?

1
function
addclass(elm,newclass){
2
var
classes=elm.className.split(
''
);
3
classes.push(newclass);
4
elm.className=classes.join(
''
);
5
}
Thisautomaticallyensuresthatclassesarespace-separatedandthatyoursgetstackedonattheend.

EventDelegation

EventsmakeWebappswork.Iloveevents,especiallycustomevents,whichmakeyourproductsextensiblewithoutyourneedingtotouchthecorecode.Themainproblem(andactuallyoneofitsstrengths)isthateventsareremovedfromtheHTML—youapplyaneventlistenertoacertainelementandthenitbecomesactive.NothingintheHTMLindicatesthatthisisthecasethough.Takethisabstractionissue(whichishardforbeginnerstowraptheirheadsaround)andthefactthat"browsers"suchasIE6haveallkindofmemoryproblemsandtoomanyeventsappliedtothem,andyou'llseethatnotusingtoomanyeventhandlersinadocumentiswise.

Thisiswhereeventdelegationcomesin.WhenaneventhappensonacertainelementandonalltheelementsaboveitintheDOMhierarchy,youcansimplifyyoureventhandlingbyusingasinglehandleronaparentelement,ratherthanusingalotofhandlers.

WhatdoImeanbythat?Sayyouwantalistoflinks,andyouwanttocallafunctionratherthanloadthelinks.TheHTMLwouldbe:

viewsource

print?

1
<h2>GreatWebresources</h2>
2
<ulid=
"resources"
>
3
<li><ahref=
"http://opera.com/wsc"
>OperaWebStandardsCurriculum</a></li>
4
<li><ahref=
"http://sitepoint.com"
>Sitepoint</a></li>
5
<li><ahref=
"http://alistapart.com"
>AListApart</a></li>
6
<li><ahref=
"http://yuiblog.com"
>YUIBlog</a></li>
7
<li><ahref=
"http://blameitonthevoices.com"
>Blameitonthevoices</a></li>
8
<li><ahref=
"http://oddlyspecific.com"
>Oddlyspecific</a></li>
9
</ul>
Thenormalwaytoapplyeventhandlersherewouldbetoloopthroughthelinks:

viewsource

print?

01
//Classiceventhandlingexample
02
(
function
(){
03
var
resources=document.getElementById(
'resources'
);
04
var
links=resources.getElementsByTagName(
'a'
);
05
var
all=links.length;
06
for
(
var
i=0;i<all;i++){
07
//Attachalistenertoeachlink
08
links[i].addEventListener(
'click'
,handler,
false
);
09
};
10
function
handler(e){
11
var
x=e.target;
//Getthelinkthatwasclicked
12
alert(x);
13
e.preventDefault();
14
};
15
})();
Thiscouldalsobedonewithasingleeventhandler:

viewsource

print?

01
(
function
(){
02
var
resources=document.getElementById(
'resources'
);
03
resources.addEventListener(
'click'
,handler,
false
);
04
function
handler(e){
05
var
x=e.target;
//getthelinktha
06
if
(x.nodeName.toLowerCase()===
'a'
){
07
alert(
'Eventdelegation:'
+x);
08
e.preventDefault();
09
}
10
};
11
})();
Becausetheclickhappensonalltheelementsinthelist,allyouneedtodoiscomparethe
nodeName
totherightelementthatyouwanttoreacttotheevent.

Disclaimer:whilebothoftheeventexamplesaboveworkinbrowsers,theyfailinIE6.ForIE6,youneedtoapplyaneventmodelotherthantheW3Cone,andthisiswhyweuselibrariesforthesetricks.

Thebenefitsofthisapproacharemorethanjustbeingabletouseasingleeventhandler.Say,forexample,youwanttoaddmorelinksdynamicallytothislist.Witheventdelegation,thereisnoneedtochangeanything;withsimpleeventhandling,youwouldhavetoreassignhandlersandre-loopthelist.

AnonymousFunctionsAndTheModulePattern

OneofthemostannoyingthingsaboutJavaScriptisthatithasnoscopeforvariables.Anyvariable,function,arrayorobjectyoudefinethatisnotinsideanotherfunctionisglobal,whichmeansthatotherscriptsonthesamepagecanaccess—andwillusuallyoverride—them.

Theworkaroundistoencapsulateyourvariablesinananonymousfunctionandcallthatfunctionimmediatelyafteryoudefineit.Forexample,thefollowingdefinitionwouldresultinthreeglobalvariablesandtwoglobalfunctions:

viewsource

print?

1
var
name=
'Chris'
;
2
var
age=
'34'
;
3
var
status=
'single'
;
4
function
createMember(){
5
//[...]
6
}
7
function
getMemberDetails(){
8
//[...]
9
}
Anyotherscriptonthepagethathasavariablenamed
status
couldcausetrouble.Ifwewrapallofthisinanamesuchas
myApplication
,thenweworkaroundthatissue:

viewsource

print?

01
var
myApplication=
function
(){
02
var
name=
'Chris'
;
03
var
age=
'34'
;
04
var
status=
'single'
;
05
function
createMember(){
06
//[...]
07
}
08
function
getMemberDetails(){
09
//[...]
10
}
11
}();
This,however,doesn'tdoanythingoutsideofthatfunction.Ifthisiswhatyouneed,thengreat.Youmayaswelldiscardthenamethen:

viewsource

print?

01
(
function
(){
02
var
name=
'Chris'
;
03
var
age=
'34'
;
04
var
status=
'single'
;
05
function
createMember(){
06
//[...]
07
}
08
function
getMemberDetails(){
09
//[...]
10
}
11
})();
Ifyouneedtomakesomeofthethingsreachabletotheoutside,thenyouneedtochangethis.Inordertoreach
createMember()
or
getMemberDetails()
,youneedtoreturnthemtotheoutsideworldtomakethempropertiesof
myApplication
:

viewsource

print?

01
var
myApplication=
function
(){
02
var
name=
'Chris'
;
03
var
age=
'34'
;
04
var
status=
'single'
;
05
return
{
06
createMember:
function
(){
07
//[...]
08
},
09
getMemberDetails:
function
(){
10
//[...]
11
}
12
}
13
}();
14
//myApplication.createMember()and
15
//myApplication.getMemberDetails()nowworks.
Thisiscalledamodulepatternorsingleton.ItwasmentionedalotbyDouglasCrockfordandisusedverymuchintheYahooUserInterfaceLibraryYUI.WhatailsmeaboutthisisthatIneedtoswitchsyntaxestomakefunctionsorvariablesavailabletotheoutsideworld.Furthermore,ifIwanttocallonemethodfromanother,Ihavetocallitprecededbythe
myApplication
name.Soinstead,IprefersimplytoreturnpointerstotheelementsthatIwanttomakepublic.Thisevenallowsmetoshortenthenamesforoutsideuse:

viewsource

print?

01
var
myApplication=
function
(){
02
var
name=
'Chris'
;
03
var
age=
'34'
;
04
var
status=
'single'
;
05
function
createMember(){
06
//[...]
07
}
08
function
getMemberDetails(){
09
//[...]
10
}
11
return
{
12
create:createMember,
13
get:getMemberDetails
14
}
15
}();
16
//myApplication.get()andmyApplication.create()nowwork.
I'vecalledthis"revealingmodulepattern."

AllowingForConfiguration

WheneverI'vewrittenJavaScriptandgivenittotheworld,peoplehavechangedit,usuallywhentheywantedittodothingsthatitcouldn'tdooutofthebox—butalsooftenbecauseImadeittoohardforpeopletochangethings.

Theworkaroundistoaddconfigurationobjectstoyourscripts.I'vewrittenaboutJavaScriptconfigurationobjectsindetail,buthere'sthegist:

Haveanobjectaspartofyourwholescriptcalled
configuration
.

Init,storeallofthethingsthatpeoplewilllikelychangewhentheyuseyourscript:

CSSIDandclassnames;

Strings(suchaslabels)forgeneratedbuttons;

Valuessuchas"numberofimagesbeingdisplayed,""dimensionsofmap";

Location,localeandlanguagesettings.

Returntheobjectasapublicpropertysothatpeoplecanoverrideit.

Mostofthetimeyoucandothisasalaststepinthecodingprocess.I'veputtogetheranexamplein"Fivethingstodotoascriptbeforehandingitovertothenextdeveloper."

Inessence,youwanttomakeiteasyforpeopletouseyourcodeandalterittotheirneeds.Ifyoudothat,youaremuchlesslikelytogetconfusingemailsfrompeoplewhocomplainaboutyourscriptsandrefertochangesthatsomeoneelseactuallydid.

InteractingWithTheBackEnd

OneofthemainthingsIlearnedfromallmyyearswithJavaScriptisthatitisagreatlanguagewithwhichtomakeinteractiveinterfaces,butwhenitcomestocrunchingnumbersandaccessingdatasources,itcanbedaunting.

Originally,IlearnedJavaScripttoreplacePerlbecauseIwassickofcopyingthingstoa
cgi-bin
folderinordertomakeitwork.Lateron,Ilearnedthatmakingaback-endlanguagedothemaindatachurningforme,insteadoftryingtodoallinJavaScript,makesmoresensewithregardtosecurityandlanguage.

IfIaccessaWebservice,IcouldgetJSON-Pasthereturnedformatanddoalotofdataconversionontheclient,butwhyshouldIwhenIhaveaserverthathasaricherwayofconvertingdataandthatcanreturnthedataasJSONorHTML…andcacheitformetoboot?

So,ifyouwanttouseAJAX,learnaboutHTTPandaboutwritingyourowncachingandconversionproxy.Youwillsavealotoftimeandnervesinthelongrun.

Browser-SpecificCodeIsAWasteOfTime.UseLibraries!

WhenIstartedWebdevelopment,thebattlebetweenusing
document.all
andusing
document.layers
asthemainwaytoaccessthedocumentwasstillraging.Ichose
document.layers
becauseIlikedtheideaofanylayerbeingitsowndocument(andIhadwrittenmorethanenough
document.write
solutionstolastalifetime).Thelayermodelfailed,butsodid
document.all
.WhenNetscape6wentalloutsupportingonlytheW3CDOMmodel,Ilovedit,butendusersdidn'tcare.Endusersjustsawthatthisbrowserdidn'tshowthemajorityoftheInternetscorrectly(althoughitdid)—thecodeweproducedwaswhatwaswrong.Webuiltshort-sightedcodethatsupportedastate-of-the-artenvironment,andthefunnythingaboutthestateoftheartisthatitisconstantlychanging.

I'vewastedquitesometimelearningtheinsandoutsofallofthebrowsersandworkingaroundtheirissues.DoingthisbackthensecuredmycareerandensuredthatIhadagreatjob.Butweshouldn'thavetogothroughthistrialbyfireanylonger.

LibrariessuchasYUI,jQueryandDojoareheretohelpuswiththis.Theytakeontheproblemsofbrowsersbyabstractingthepainsofpoorimplementation,inconsistenciesandflat-outbugs,andrelieveusofthechore.Unlessyouwanttobetatestacertainbrowserbecauseyou'reabigfan,don'tfixbrowserissuesinyourJavaScriptsolutions,becauseyouareunlikelytoeverupdatethecodetoremovethisfix.AllyouwouldbedoingisaddingtothealreadymassivepileofoutdatedcodeontheWeb.

Thatsaid,relyingsolelyonlibrariesforyourcoreskillisshort-sighted.ReaduponJavaScript,watchsomegoodvideosandtutorialsonit,andunderstandthelanguage.(Tip:closuresareGod'sgifttotheJavaScriptdeveloper.)Librarieswillhelpyoubuildthingsquickly,butifyouassignalotofeventsandeffectsandneedtoaddaclasstoeveryHTMLelementinthedocument,thenyouaredoingitwrong.

Resources

Inadditiontotheresourcesmentionedinthisarticle,alsocheckoutthefollowingtolearnmoreaboutJavaScriptitself:

DouglasCrockfordonJavaScript
Anin-depthvideoLectureseries.

TheOperaWebStandardsCurriculum
WithadetailedsectiononJavaScript.

RelatedPosts

Youmaybeinterestedinthefollowingrelatedposts:

TheSevenDeadlySinsOfJavaScriptImplementation

DevelopingSitesWithAJAX:DesignChallengesandCommonIssues

45PowerfulCSS/JavaScript-Techniques
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: