FunktionerFunktioner är kodblock som utför vissa uppgifter och som kan återanvändas i programmet. Det finns två typer av funktioner i ActionScript 3.0: metoder och funktionsslut. Huruvida en funktion kallas metod eller funktionsslut beror på det sammanhang som funktionen definierats i. En funktion kallas metod om du definierar den som en del av en klassdefinition eller bifogar den till en instans av ett objekt. En funktion kallas funktionsslut om den har definierats på något annat sätt. Funktioner har alltid varit oerhört viktiga i ActionScript. I ActionScript 1.0 fanns inte nyckelordet class, och ”klasser” definierades av konstruktorfunktioner. Även om nyckelordet class sedan dess har lagts till i språket är det fortfarande viktigt att du känner till hur funktioner fungerar om du vill få ut det mesta av språket. Detta kan vara en utmaning för programmerare som förväntar sig att ActionScript-funktioner ska bete sig på samma sätt som funktioner i C++ eller Java. Även om grundläggande funktionsdefinition och funktionsanrop inte är någon utmaning för erfarna programmerare, kräver ändå vissa av de avancerade funktionerna i ActionScript en viss förklaring. Grundläggande funktionsbegreppAnropsfunktionerDu kan anropa en funktion med dess identifierare följt av parentesoperatorn (()). Använd parentesoperatorn för att omsluta de funktionsparametrar som ska skickas till funktionen. Funktionen trace() är till exempel en funktion på översta nivån i ActionScript 3.0: trace("Use trace to help debug your script"); Om du anropar en funktion som saknar parametrar måste du använda en tom parentes. Du kan till exempel använda metoden Math.random(), som inte tar någon parameter, för att generera ett slumpmässigt tal: var randomNum:Number = Math.random(); Definiera egna funktionerDu kan definiera en funktion på två sätt i ActionScript 3.0: du kan använda en funktionssats eller ett funktionsuttryck. Vilken teknik du använder beror på om du vill ha en statisk eller dynamisk programmeringsstil. Om du föredrar statisk programmering, s.k. strikt läge, definierar du funktioner med funktionssatser. Om du har ett särskilt behov av att använda funktionsuttryck definierar du funktioner med dessa. Funktionsuttryck används oftare i dynamisk programmering, s.k. standardläge. FunktionssatserFunktionssatser används mest vid definition av funktioner i strikt läge. En funktionssats börjar med nyckelordet function följt av:
Följande kod skapar en funktion som definierar en parameter och sedan anropar funktionen med strängen ”hello” som parametervärde: function traceParameter(aParam:String) { trace(aParam); } traceParameter("hello"); // hello FunktionsuttryckDet andra sättet att deklarera en funktion är att använda en tilldelningsprogramsats med ett funktionsuttryck, vilken ibland kallas funktionsliteral eller anonym funktion. Detta är en mer detaljerad metod som används mycket i tidigare versioner av ActionScript. En tilldelningssats med ett funktionsuttryck börjar med nyckelordet var följt av:
Välj mellan programsatser och uttryckSom allmän regel gäller att du använder en funktionssats om du inte särskilt måste använda ett uttryck. Funktionssatser är mindre detaljerade och de är mera konsekventa vad gäller strikt läge och standardläge än funktionsuttryck. Funktionssatser är lättare att läsa än tilldelningssatser som innehåller funktionsuttryck. Funktionssatser gör koden mera koncis. De är mindre krångliga än funktionsuttryck, som kräver att du använder både nyckelordet var och function. Funktionssatser är mera konsekventa vad beträffar de två kompilatorlägena, eftersom du kan använda punktsyntax i både strikt läge och standardläge för att anropa en metod som deklarerats med en funktionssats. Detta är inte alltid säkert när det gäller metoder som deklarerats med ett funktionsuttryck. I följande kod definieras klassen Example med två metoder: methodExpression() som deklareras med ett funktionsuttryck samt methodStatement() som deklareras med en funktionssats. I strikt läge kan du inte använda punktsyntax för att anropa metoden methodExpression(). class Example { var methodExpression = function() {} function methodStatement() {} } var myEx:Example = new Example(); myEx.methodExpression(); // error in strict mode; okay in standard mode myEx.methodStatement(); // okay in strict and standard modes Funktionsuttryck passar bättre för programmering som fokuseras på körningsbeteende, s.k. dynamiskt beteende. Om du föredrar strikt läge men ändå måste anropa en metod som deklarerats med ett funktionsuttryck, kan du använda vilken teknik du vill. Du kan anropa metoden med hjälp av hakparentes ([]) i stället för punktoperatorn (.). Följande metodanrop lyckas både i strikt läge och i standardläge: myExample["methodLiteral"](); Du kan också deklarera hela klassen som en dynamisk klass. Även om detta gör att du kan anropa metoden med punktoperatorn, är nackdelen att du offrar vissa funktioner i strikt läge för alla instanser av den här klassen. Kompilatorn genererar inte något fel om du försöker komma åt en odefinierad egenskap för en instans av en dynamisk klass. I vissa lägen är funktionsuttryck mycket användbara. Ofta används funktionsuttryck för funktioner som används bara en gång och sedan tas bort. Ett ovanligare användning är att bifoga en funktion till en prototypegenskap. Mer information finns i Objektet prototype. Det finns två små skillnader mellan funktionssatser och funktionsuttryck som du måste tänka på när du väljer vilken teknik du ska använda. Den första skillnaden är att funktionsuttryck inte existerar oberoende som objekt med hänsyn till minneshantering och skräpsamling. När du tilldelar ett annat objekt, t.ex. ett array-element eller en objektegenskap, ett funktionsuttryck skapar du bara en referens till funktionsuttrycket i koden. Om den array eller det objekt som funktionsuttrycket är kopplat till hamnar utanför omfånget eller på annat sätt blir otillgängligt, kan du inte komma åt funktionsuttrycket. Om arrayen eller objektet tas bort blir det minne som funktionsuttrycket använder godkänt för skräpsamling, vilket betyder att minnet kan återvinnas och återanvändas för andra ändamål. I följande exempel visas, att för ett funktionsuttryck är funktionen inte längre tillgänglig när egenskapen som uttrycket är kopplat till tas bort. Klassen Test är dynamisk, vilket betyder att du kan lägga till egenskapen functionExp som innehåller ett funktionsuttryck. Funktionen functionExp() kan anropas med punktoperatorn, men när egenskapen functionExp tas bort är funktionen inte längre tillgänglig. dynamic class Test {} var myTest:Test = new Test(); // function expression myTest.functionExp = function () { trace("Function expression") }; myTest.functionExp(); // Function expression delete myTest.functionExp; myTest.functionExp(); // error Om å andra sidan funktionen först definieras med en funktionssats existerar den som ett eget objekt och fortsätter att existera även när du tar bort egenskapen som funktionen är kopplad till. Operatorn delete fungerar bara på egenskaper för objekt, och inte ens ett anrop för att ta bort själva funktionen stateFunc() fungerar. dynamic class Test {} var myTest:Test = new Test(); // function statement function stateFunc() { trace("Function statement") } myTest.statement = stateFunc; myTest.statement(); // Function statement delete myTest.statement; delete stateFunc; // no effect stateFunc();// Function statement myTest.statement(); // error Den andra skillnaden mellan funktionssatser och funktionsuttryck är att funktionssatser existerar i hela omfånget som de definierats i, däribland programsatser som visas före funktionssatsen. Funktionsuttryck definieras däremot bara för efterföljande programsatser. I följande kod anropas funktionen scopeTest() innan den definieras: statementTest(); // statementTest function statementTest():void { trace("statementTest"); } Funktionsuttryck är inte tillgängliga innan de definieras, och följande kod genererar ett körningsfel: expressionTest(); // run-time error var expressionTest:Function = function () { trace("expressionTest"); } Returnera värden från funktionerOm du vill returnera ett värde från funktionen använder du programsatsen return följt av det uttryck eller literala värde du vill returnera. Följande kod returnerar ett uttryck som representerar parametern: function doubleNum(baseNum:int):int { return (baseNum * 2); } Observera att programsatsen return avbryter funktionen, vilket innebär att programsatser under en return-programsats inte körs, enligt följande: function doubleNum(baseNum:int):int { return (baseNum * 2); trace("after return"); // This trace statement will not be executed. } I strikt läge måste du returnera ett värde för en lämplig typ om du väljer att ange en returtyp. I följande kod genereras ett fel i strikt läge eftersom ett giltigt värde inte returneras: function doubleNum(baseNum:int):int { trace("after return"); } Kapslade funktionerDu kan kapsla funktioner, vilket betyder att funktioner kan deklareras inuti andra funktioner. En kapslad funktion är bara tillgänglig inuti dess överordnade funktion, såvida inte en referens till funktionen skickas till extern kod. I följande kod deklareras två kapslade funktioner inuti funktionen getNameAndVersion(): function getNameAndVersion():String { function getVersion():String { return "10"; } function getProductName():String { return "Flash Player"; } return (getProductName() + " " + getVersion()); } trace(getNameAndVersion()); // Flash Player 10 När kapslade funktioner skickas till extern kod skickas de som funktionsslut, vilket betyder att funktionen sparar alla definitioner som finns i omfånget när funktionen definieras. Mer information finns i Funktionsomfång. FunktionsparametrarI ActionScript 3.0 finns vissa funktioner för funktionsparametrar som kan verka vara nyheter för programmerare som är nybörjare i språket. Även om begreppet att skicka parametrar med värde eller referens är känt för de flesta programmerare, är kanske argumentobjektet och... (rest)-parametern ny för många. Överföra argument med värde eller referensI många programmeringsspråk är det viktigt att förstå skillnaden mellan att skicka argument med värde eller med referens. Skillnaden kan påverka sättet som koden utformas på. Att skickas med värde betyder att värdet för argumentet kopieras till en lokal variabel som ska användas i funktionen. Att skickas med referens betyder att bara en referens till argumentet skickas, i stället för det verkliga värdet. Ingen kopia av argumentet görs. I stället skapas en referens till den variabel som skickas som ett argument och den kopplas till en lokal variabel som ska användas i funktionen. Med den lokala variabeln, som är en referens till en variabel utanför funktionen, kan du ändra värdet på ursprungsvariabeln. I ActionScript 3.0 skickas alla argument med referens, eftersom alla värden lagras som objekt. Objekt som tillhör primitiva datatyper, däribland Boolean, Number, int, uint och String, har speciella operatorer som gör att de beter sig som om de skickats med värde. Med följande kod skapas till exempel funktionen passPrimitives() som definierar två parametrar, xParam och yParam, som båda är av typen int. Dessa parametrar liknar lokala variabler som deklarerats i texten för funktionen passPrimitives(). När funktionen anropas med argumenten xValue och yValue initieras parametrarna xParam och yParam med referenser till int-objekt som representeras av xValue och yValue. Eftersom argumenten är primitiva beter de sig som om de skickats med värde. Även om xParam och yParam initialt bara innehåller referenser till objekten xValue och yValue, genererar alla ändringar i variablerna i funktionstexten nya kopior av värdena i minnet. function passPrimitives(xParam:int, yParam:int):void { xParam++; yParam++; trace(xParam, yParam); } var xValue:int = 10; var yValue:int = 15; trace(xValue, yValue);// 10 15 passPrimitives(xValue, yValue); // 11 16 trace(xValue, yValue);// 10 15 I funktionen passPrimitives() ökas värdena för xParam och yParam, men detta påverkar inte värdena för xValue och yValue, vilket visas i den senaste trace-programsatsen. Detta skulle vara fallet även om parametrarna hade exakt samma namn som variablerna, xValue och yValue, eftersom xValue och yValue inuti funktionen pekar på nya platser i minnet som ligger åtskilda från variablerna med samma namn utanför funktionen. Alla andra objekt, d.v.s. objekt som inte hör till de primitiva datatyperna, skickas alltid med referens, vilket gör att du kan ändra värdet på ursprungsvariabeln. I följande kod skapas objektet objVar med de två egenskaperna x och y. Objektet skickas som ett argument till funktionen passByRef(). Eftersom objektet inte är av primitiv typ skickas det med referens och stannar som referens. Detta betyder att ändringar av parametrarna i funktionen påverkar objektets egenskaper utanför funktionen. function passByRef(objParam:Object):void { objParam.x++; objParam.y++; trace(objParam.x, objParam.y); } var objVar:Object = {x:10, y:15}; trace(objVar.x, objVar.y); // 10 15 passByRef(objVar); // 11 16 trace(objVar.x, objVar.y); // 11 16 Parametern objParam refererar samma objekt som den globala variabeln objVar. Som du kan se av programsatsen trace i exemplet återspeglas ändringar i egenskaperna x och y för objektet objParam i objektet objVar. Standardvärden för parametrarI ActionScript 3.0 kan du deklarera standardparametervärden för en funktion. Om ett anrop till en funktion med standardvärden för parametrar utesluter en parameter med standardvärden, används värdet som anges i funktionsdefinitionen för denna parameter. Alla parametrar med standardvärden måste placeras i slutet av parameterlistan. De värden som angetts som standardvärden måste vara kompileringskonstanter. Om det finns ett standardvärde för en parameter blir denna parameter en valfri parameter. En parameter utan standardvärde betraktas som en obligatorisk parameter. I följande kod skapas en funktion med tre parametrar, varav två har standardvärden. När funktionen anropas med bara en parameter, används standardvärdena för denna parameter. function defaultValues(x:int, y:int = 3, z:int = 5):void { trace(x, y, z); } defaultValues(1); // 1 3 5 Objektet ArgumentsNär parametrar skickas till en funktion, kan du använda objektet arguments för att komma åt information om de parametrar som skickats till funktionen. Vissa aspekter om argument är viktiga:
Parametern... (rest)I ActionScript 3.0 introduceras en ny parameterdeklaration som kallas parametern... (rest). Med den här parametern kan du ange en arrayparameter som godkänner valfritt antal kommaavgränsade parametrar. Parametern kan heta vad som helst bara det inte är ett reserverat ord. Parameterdeklarationen måste vara den sist angivna parametern. Om du använder den här parametern kan du inte använda objektet arguments. Även om parametern ... (rest) har samma funktioner som arrayen arguments och egenskapen arguments.length har den inte samma funktioner som arguments.callee. Du måste kontrollera att du inte behöver använda argument.callee innan du använder parametern ... (rest). I följande exempel skrivs funktionen traceArgArray() om med hjälp av ... (rest)-parametern i stället för objektet arguments: function traceArgArray(... args):void { for (var i:uint = 0; i < args.length; i++) { trace(args[i]); } } traceArgArray(1, 2, 3); // output: // 1 // 2 // 3 Parametern... (rest) kan också användas med andra parametrar så länge som den är den sista parametern. I följande exempel ändras funktionen traceArgArray() så att dess första parameter, x, är av typen int och den andra parametern använder parametern ... (rest). Det första värdet finns inte med i utdata eftersom den första parametern inte längre ingår i arrayen som skapats av parametern ... (rest). function traceArgArray(x: int, ... args) { for (var i:uint = 0; i < args.length; i++) { trace(args[i]); } } traceArgArray(1, 2, 3); // output: // 2 // 3 Funktioner som objektFunktioner i ActionScript 3.0 är objekt. När du skapar en funktion skapar du ett objekt, som inte bara kan skickas som parameter till en annan funktion, utan även få egenskaper och metoder kopplade till sig. Funktioner som skickas som argument till andra funktioner skickas med referens och inte med värde. När du skickar en funktion som ett argument använder du bara identifieraren och inte parentesoperatorn som du använder för att anropa metoden. Med följande kod skickas funktionen clickListener() som ett argument till metoden addEventListener(): addEventListener(MouseEvent.CLICK, clickListener); Även om du som inte använt ActionScript tidigare tycker att det är märkligt, kan funktioner ha egenskaper och metoder som vilka andra objekt som helst. Alla funktioner har i själva verket en skrivskyddad egenskap som heter length som lagrar antalet parametrar som definierats för funktionen. Egenskapen arguments.length rapporterar i stället antalet argument som skickas till funktionen. Kom ihåg, att i ActionScript kan antalet argument som skickas till en funktion överstiga antalet parametrar som definierats för samma funktion. I följande exempel, som bara kompileras i standardläge eftersom strikt läge kräver en exakt överensstämmelse mellan antalet skickade argument och antalet definierade parametrar, visas skillnaden mellan de två egenskaperna: // Compiles only in standard mode function traceLength(x:uint, y:uint):void { trace("arguments received: " + arguments.length); trace("arguments expected: " + traceLength.length); } traceLength(3, 5, 7, 11); /* output: arguments received: 4 arguments expected: 2 */ I standardläget kan du definiera egna funktionsegenskaper genom att definiera dem utanför funktionstexten. Funktionsegenskaperna kan fungera som kvasistatiska egenskaper som du använder för att spara en variabel som relateras till funktionen. Du kanske vill spåra hur många gånger en viss funktion anropas. Du kan använda den här funktionen om du skriver ett spel och vill spåra hur många gånger en användare använder ett visst kommando, men du kan också använda en statisk klassegenskap för detta. I följande exempel, som bara kompileras i standardläge eftersom du inte kan lägga till dynamiska egenskaper i funktioner i strikt läge, skapas en funktionsegenskap utanför funktionsdeklarationen och egenskapen ökar stegvis varje gång som funktionen anropas: // Compiles only in standard mode var someFunction:Function = function ():void { someFunction.counter++; } someFunction.counter = 0; someFunction(); someFunction(); trace(someFunction.counter); // 2 FunktionsomfångMed en funktions omfång anges var i programmet funktionen kan anropas men också vilka funktionsdefinitioner som funktioner har åtkomst till. Samma omfångsregler som gäller för variabelidentifierare gäller också för funktionsidentifierare. En funktion som deklareras i det globala omfånget kan användas i hela koden. ActionScript 3.0 innehåller globala funktioner, till exempel isNaN() och parseInt(), som är tillgängliga överallt i koden. En kapslad funktion, d.v.s. en funktion som har deklarerats inuti en annan funktion, kan användas var som helst i den funktion som den har deklarerats i. OmfångskedjanVarje gång en funktion körs skapas ett antal objekt och egenskaper. Först skapas ett speciellt objekt som kallas aktiveringsobjekt som lagrar parametern och alla lokala variabler eller funktioner som deklarerats i funktionstexten. Du kan inte komma åt aktiveringsobjektet direkt eftersom det är en intern mekanism. Därefter skapas en omfångskedja, som innehåller en ordnad lista på objekt som vid körning genomsöks efter identifierardeklarationer. Varje funktion som körs har en omfångskedja som lagras i en intern egenskap. Om det är en kapslad funktion startar omfångskedjan med det egna aktiveringsobjektet, följt av aktiveringsobjektet i den överordnade funktionen. Kedjan fortsätter på det här sättet tills den når det globala objektet. Det globala objektet skapas när ett ActionScript-program startar och objektet innehåller alla globala variabler och funktioner. FunktionsslutEtt funktionsslut är ett objekt som innehåller en ögonblicksbild av en funktion och dess lexikala miljö. En funktions lexikala miljö innehåller alla variabler, egenskaper, metoder och objekt i funktionens omfångskedja, tillsammans med deras värden. Funktionsslut skapas varje gång en funktion körs skilt från ett objekt eller en klass. Det faktum att funktionsslutet sparar omfånget som det definierats i, skapar intressanta resultat när en funktion skickas som ett argument eller ett returvärde till ett annat omfång. I följande kod skapas två olika funktioner: foo(), som returnerar den kapslade funktionen rectArea() som beräknar arean av en rektangel, samt bar() som anropar foo() och lagrar det returnerade funktionsslutet i variabeln myProduct. Även om funktionen bar() definierar sin egen lokala variabel x (med värdet 2) när funktionsslutet myProduct() anropas, sparas variabeln x (med värdet 40) som definierats i funktionen foo(). Funktionen bar() returnerar därför värdet 160 i stället för 8. function foo():Function { var x:int = 40; function rectArea(y:int):int // function closure defined { return x * y } return rectArea; } function bar():void { var x:int = 2; var y:int = 4; var myProduct:Function = foo(); trace(myProduct(4)); // function closure called } bar(); // 160 Metoder beter sig på ungefär samma sätt eftersom de också sparar information om den lexikala miljö som de skapats i. Dessa egenskaper märks mest när en metod extraheras från sin instans, vilket skapar en bunden metod. Den största skillnaden mellan ett funktionsslut och en bunden metod är att värdet för nyckelordet this i en bunden metod alltid refererar till instansen som den ursprungligen var kopplad till, medan värdet för nyckelordet this kan ändras i ett funktionsslut. |
|