INTRODUCTION TO JavaScript Functions


DECLARING A FUNCTION


There are several ways to declare a function.  All valid, but there are differences on how they're handled in the background.

  • The usual way to declare a function is using this syntax:

    functionName([parameters]){functionBody};

    Example D1

    function add(a, b)
                                {
                                return a+b;
                                }
                                alert(add(1,2));        // produces 3

    Run example. 
       
    When we declare function like this, the content of the function is compiled (but not executed until we call the function).  Also, you might not know it, but an object with the same name as the function is created.  In the above example, we have an object named "add" (for more on this, see Function as an Object section below.)  
     


  • We can also declare a function by assigning a variable to an unnamed function.

    Example D2

    var add=function(a, b)
                                {
                                return a+b;
                                }
                                alert(add(1,2));        // produces 3

    Run example.  
    This code does the same thing as the previous example.  The syntax may seem odd, but it may make more sense when you consider that a function is an object, and we're just assigning a name to the object.  Think of it as saying var myVar=[1,2,3];  The content of functions declared this way is also compiled.  

When assigning a function like this, we are not restricted to an unnamed function.  Here, I did the same thing as ExampleD2, but I added "theAdd" as the function name, and I can refer to the function by the name or by the variable.

Example D2A:

var add=function theAdd(a, b)
                        {
                        return a+b;
                        }
                        alert(add(1,2));           // produces 3
                        alert(theAdd(1,2));        // also produces 3

Run example.  

The ability to declare function in this way is useful in object oriented programming, because we can have a function be a property of an object, like this.

var myObject=new Object();
                        myObject.add=function(a,b){return a+b};  
                        // myObject now has a property/a method named "add"
                        // and I can use it like below
                        myObject.add(1, 2);



  • We can also declare a function using the "new" operator.  This is the least common way of declaring a function and is not recommended unless there is a specific reason to use it (see below for possible reasons).  The syntax is as follows: 
    varName=new Function([param1Name, param2Name,...paramNName], functionBody);
    Example D3:
    var add=new Function("a", "b", "return a+b;");
                                alert(add(3,4));        // produces 7
    Run example. 
       
    Here I have 2 parameters named a and b, the functionBody returns the sum of a and b.  Note that "new Function(...)" uses capital F, not lower case f.  This tells JavaScript that we want to create an object of type FunctionNote also that the parameter names and the function body are passed as Strings.  We can have as many parameters as we want, JavaScript knows that the function body is the String right before the closing bracket.   (If there's no parameter, you can just have the function body.)  You do not have to put everything on a single line (use "/" or use String concatenation "+" to separate long code).  The "/" sign tells JavaScript to look for the remaining of the string on the next line.  An example is shown below.

    Example D4
    var add=new Function("a", "b",
                                "alert" +                      // chop string using "+"
                                "('adding '+a+' and ' +b);/    // separate string using "/"
                                return a+b;");
                                alert(add(3,4));        // produces 7
    Run example.  
      
    Declaring function in this way causes the function not to be compiled, and is potentially slower than the other ways of declaring functions.  To see why, consider this code:  

    Example D5
    function createMyFunction(myOperator)
                                {
                                return new Function("a", "b", "return a" + myOperator + "b;");
                                }
                                var add=createMyFunction("+");                // creates "add" function
                                var subtract=createMyFunction("-");           // creates "subtract" function
                                var multiply=createMyFunction("*");           // created "multiply" function
    // test the functions
                                alert("result of add="+add(10,2));            // result is 12
                                alert("result of substract="+subtract(10,2)); // result is 8
                                alert("result of multiply="+multiply(10,2));  // result is 20
                                alert(add);
                                
    Run example.  
      
    That interesting example creates 3 different functions during runtime by passing a parameter which will be used to build a new Function.  Since the compiler has no way of knowing what the final code will look like, the content of new Function(...) is not compiled.  What good is this for?  Well, an example of where this feature might be useful is when you want to allow the user to create their own function; such as in a game.  We might want to allow user to add "behaviors" to a "player."  But again, in generally, we should avoid using this format unless there's a specific purpose.
FUNCTION AS AN OBJECT


Function is a special kind of object in JavaScript, it's a first class data type.  That means we can add properties to it.  Here are some interesting points to note:

  • As mentioned briefly before, when we declare a function, JavaScript actually creates an object for you in the background.  The name of the object is the function name itself.  The type of the object is function.  In the example below, we might not realize it, but we actually have created an object called: Ball.   
    Example 1
    function Ball()       // it may seem odd, but this declaration
                                {                     // creates a object named Ball
                                i=1;
                                }
                                alert(typeof Ball);     // produces "function"
    Run example.  

    We can even print the content of the object and it will output the actual code of the function:  Example 2: Click alert(Ball); to see content of Ball.


  • We can add properties to Objects, including function objects.  Since the act of declaring a function creates an object.  We can, potentially add properties to that function.  For example, here we declare function Ball, and added callsign property.
    Example 3
    function Ball()       // it may seem odd, but declaration
                                {                     // creates an object named Ball, and you can
                                }                     // refer to it or add properties to it like below
                                Ball.callsign="The Ball"; // add property to Ball
                                alert(Ball.callsign); // produces "The Ball"
    Run example.
    >
  • Since function is an object, we can assign a pointer to a function.  For example, below, the variable ptr points to myFunction object.
    Example 4

    function myFunction(message)
                                {
                                alert(message);
                                }
                                var ptr=myFunction;  // ptr points to myFunction
                                ptr("hello");      // executes myFunction which will prints "hello"
    Run example.

    We can execute the function as if the function name has been replaced by the pointer name.  So above, the line ptr("hello"); has the same meaning as this: myFunction("hello");

    Pointer to a function is useful in object oriented programming.  For instance: when we want several objects to point to the same function like below:
    Example 4A

    function sayName(name)
                                {
                                alert(name);
                                }
    var object1=new Object();      // creates 3 objects
                                var object2=new Object();
                                var object3=new Object();
                                object1.sayMyName=sayName;       // assign the function to all objects
                                object2.sayMyName=sayName;
                                object3.sayMyName=sayName;
                                object1.sayMyName("object1");    // prints "object1"
                                object2.sayMyName("object2");    // prints "object2"
                                object3.sayMyName("object3");    // prints "object3"
    Run example.


    Since only pointer is stored (not the actual function itself), when we change the function object itself, all pointers pointing to that function will see the changes.  We can see it here:
    Example 5:

    function myFunction()
                                {
                                alert(myFunction.message);
                                }
                                myFunction.message="old";
                                var ptr1=myFunction;                 // ptr1 points to myFunction
                                var ptr2=myFunction;                 // ptr2 also points to myFunction
                                ptr1();         // prints "old"
                                ptr2();                              // prints "old"
                                myFunction.message="new";
                                ptr1();         // prints "new"
                                ptr2();                              // prints "new"
    Run example.
     
  • We can reassign function after you created it, but we need to refer to the function object itself, not a pointer to it.  For example, below I changed the content of myfunction().
    Example 6:

    function myFunction()
                                {
                                alert("Old");
                                }
                                myFunction(); // prints "Old"
    myFunction=function()
                                {
                                alert("New");
                                };
                                myFunction(); // prints "New"
    Run example.

    Where has the old function gone?  Into oblivion.  

    If we want to save it, we can assign a pointer to it before changing it.

    Example 6A:
    function myFunction()
                                {
                                alert("Old");
                                }
                                var savedFuncion=myFunction;
    myFunction=function()
                                {
                                alert("New");
                                };
                                myFunction();    // prints "New"
                                savedFuncion();  // printf "Old"
    Run example.

    Be careful, the example below won't work because instead of modifying the function, the code below creates another function which is called myFunctionPtr.
    Example 6B:

    function myFunction()
                                {
                                alert("Old");
                                }
                                var savedFunc=myFunction;
                                savedFunc=function()
                                {
                                alert("New");
                                };
                                myFunction();            // prints "Old"
                                savedFunc();             // prints "New"
    Run example.
     

  • We can nest a function within a function.  For example, below I have a function called getHalfOf, and within it I have another function called calculate.  
    Example 7
    function getHalfOf(num1, num2, num3)
                                {
                                function calculate(number)
                                {
                                return number/2;
                                  }
                                var result="";
                                result+=calculate(num1)+" ";
                                result+=calculate(num2)+" ";
                                result+=calculate(num3);
                                }
                                var resultString=getHalfOf(10,20,30);
                                alert(resultString);         // prints "5 10 15"
    Run example.

    You can only call a nested function locally.  That is, you cannot call: getHalfOf.calculate(10) because calculate only exists when the outer function (getHalfOf()) is run.  This is consistent with our discussion above (function is compiled, but not executed until you call it). 


  • You might be wondering about naming collision.  For example, which calculate function will be called below?
    Example 8
    function calculate(number)
                                {
                                return number/3;
                                }
                                function getHalfOf(num1, num2, num3)
                                {
                                function calculate(number)
                                {
                                return number/2;
                                  }
                                var result="";
                                result+=calculate(num1)+" ";
                                result+=calculate(num2)+" ";
                                result+=calculate(num3);
                                }
                                var resultString=getHalfOf(10,20,30);
                                alert(resultString);         // prints "5 10 15"
    Run example.

    In cases like this, the compiler will first look on the local address space, so it will use the nested calculate function.  If we erase the nested/local calculate function, then that code will use the global calculate function.

FUNCTIONS AS A DATA TYPE AND FUNCTION CONSTRUCTOR

Let's look at another special feature of a function that makes it unique among other object types.  A function can be used as a blueprint for a data type.  This feature is commonly used in object oriented programming to simulate user defined data type.  Objects created with user defined data type is usually referred to as user defined objects.
  • By declaring a function, we have also created a new data type.  The data type can be used to create a new object.  For example, below I created a new data type named Ball
    Example DT1

    function Ball()
                                {
                                }
                                var ball0=new Ball(); // ball0 now points to a new object
                                alert(ball0);         // prints "Object" because ball0 is now an Object
    Run example.
      
    So what does "ball0=new Ball()" do?  The "new" keyword creates a new object (named ball0) of type Object.  It then executes: Ball(), passing the reference to ball0 as the calling object.  Below, you will see the message: "creating new Ball" if indeed Ball() is executed.
    Example DT2
    function Ball(message)
                                {
                                alert(message);
                                }
                                var ball0=new Ball("creating new Ball");  // creates object &
                                // prints the message
                                ball0.name="ball-0";                      // ball0 now has a "name" property
                                alert(ball0.name);                        // prints "ball-0"
    Run example.

    We may consider the red portion of the above code as a shortcut for doing the same thing as below:
    Example DT2A
    function Ball(message)
                                {
                                alert(message);
                                }
                                var ball0=new Object();
                                ball0.construct=Ball;
                                ball0.construct("creating new ball");  // executes ball0.Ball("creating..");
                                ball0.name="ball-0";
                                alert(ball0.name);                        
    Run example.

    The line ball0.construct=Ball has the same syntax as ptr=myFunction in Example 4
    Review Example 4 also if you're not sure what the line after that means.  Note: You might be thinking of doing ball0.Ball("...") directly, but that won't work, because ball0 have no property named Ball("...") and it won't know what you're trying to do.


  • When we create an object using the new keyword like above, a new Object is created.   We can add properties to the object after the creation (such as when I added "name" property above.  The problem with that approach is that if we create another instance of the object, we need to add the property again to the new object like below.   
    Example DT3
    (creates 3 ball objects)
    function Ball()
                                {
                                }
                                var ball0=new Ball(); // ball0 now points to a new instance of type Ball
                                ball0.name="ball-0";  // ball0 now has a "name" property
                                var ball1=new Ball();
                                ball1.name="ball-1";
                                var ball2=new Ball();
                                alert(ball0.name);    // prints "ball-0"
                                alert(ball1.name);    // prints "ball-1"
                                alert(ball2.name);    // oops, I forgot to add "name" to ball2!

    I forgot to add name to ball2 there, which may cause problem on a real program.  Would it be nice if there's a way to add properties automatically?  Well there is: using the "this" keyword.  The word "this" has a special meaning within a function.  It refers to the object calling that function.  Let's see another example below, this time, we add the properties on the constructor function:  
    Example DT4
    function Ball(message, specifiedName)
                                {
                                alert(message);
                                this.name=specifiedName;
                                }
                                var ball0=new Ball("creating new Ball", "Soccer Ball");
                                alert(ball0.name);                   // prints "Soccer Ball"
    Run example.

    Remember that the "new" keyword eventually causes the constructor function to be executed.  In this case, it will executel Ball("creating new Ball", "Soccer Ball"); and the
    keyword this will refer to ball0
    Therefore, the line: this.name=specifiedName becomes ball0.name="Soccer Ball".  
    It basically said: add "name" property to ball0, with the value of "Soccer Ball."

    So now, we've just added a name property to ball0, much like what is being done in previous examples, but in much more elegant and extensible way.  Now, we can have as many balls and as many properties as we want without having to add them manually.  Also, people wanting to create a Ball object can easily see the constructor function and easily find out all the properties of a Ball.  Let's add more properties to Ball.
    Example DT5

    function Ball(color, specifiedName, owner, weight)
                                {
                                this.name=specifiedName;
                                this.color=color;
                                this.owner=owner;
                                this.weight=weigth;
                                }
                                var ball0=new Ball("black/white", "Soccer Ball", "John", 20);
                                var ball1=new Ball("gray", "Bowling Ball", "John", 30);
                                var ball2=new Ball("yellow", "Golf Ball", "John", 55);
                                var balloon=new Ball("red", "Balloon", "Pete", 10);
                                alert(ball0.name);                        // prints "Soccer Ball"
                                alert(balloon.name);                      // prints "Balloon"
                                alert(ball2.weight);                      // prints "55"
    Run example.

    Whew!  In object oriented term, you can say that Ball is an object type which has the following properties: name, color, owner, weight
     
  • We are not limited to adding simple data types such as Strings or numbers as properties.  We can also assign objects as properties.  Below, supervisor is a property of Employee.   Example DT6
    function Employee(name, salary, mySupervisor)
                                {
                                this.name=name;
                                this.salary=salary;
                                this.supervisor=mySupervisor;
                                }
                                var boss=new Employee("John", 200);
                                var manager=new Employee("Joan", 50, boss);
                                var teamLeader=new Employee("Rose", 50, boss);
                                alert(manager.supervisor.name+" is the supervisor of "+manager.name);
                                alert(manager.name+"/'s supervisor is "+manager.supervisor.name);  
    What will this print?  Run example.
    As you see in the above example, manager and teamLeader both has a property supervisor, which is an object of type Employee.  
     
  • Any kind of object can be a property, in fact, recall from Example 4 (not Example DT4) above, that a function is also an object.  So you can make a function a property of an object.  Below, I added 2 functions getSalary and addSalary
    Example DT7
    function Employee(name, salary)
                                {
                                this.name=name;
                                this.salary=salary;
                                this.addSalary=addSalaryFunction;
                                this.getSalary=function()
                                {
                                return this.salary;
                                };
                                }
    function addSalaryFunction(addition)
                                {
                                this.salary=this.salary+addition;
                                }
                                var boss=new Employee("John", 200000);
                                boss.addSalary(10000);                    // boss gets 10K raise
                                alert(boss.getSalary());                  // print 210K

    Run example.

    addSalary and getSalary demonstrates several different ways of assigning functions as properties.  If you remember our discussion at the very beginning; I talked about three different ways of declaring functions.  All of them are applicable here, but the two ways shown above are the most common.   

    Let's see what the differences are.  Below, take a look at the red portion of the same example from above.   When this piece of code is executed, getSalary function is declared.  As have been mentioned several times, a function declaration causes an object to be created.  So by the time boss is created (after the green line below), boss has a property called getSalary somewhere.    

    function Employee(name, salary)
                                {
                                this.name=name;
                                this.salary=salary;
                                this.addSalary=addSalaryFunction;
                                  this.getSalary=function()
                                {
                                return this.salary;
                                };
                                }
    function addSalaryFunction(addition)
                                {
                                this.salary=this.salary+addition;
                                }
                                var boss=new Employee("John", 200000);
                                var boss2=new Employee("Joan", 200000);
                                var boss3=new Employee("Kim", 200000);

    When you create more instances of the objects (boss2 and boss3), each of those instances will have a separate copy of the getSalary code; whereas addSalary points to only one location (which is the addSalaryFunction).  

    See example below to see the effect described above.
    Example DT8

    function Employee(name, salary)
                                {
                                this.name=name;
                                this.salary=salary;
                                this.addSalary=addSalaryFunction;
                                this.getSalary=function()
                                {
                                return this.salary;
                                };
                                }
    function addSalaryFunction(addition)
                                {
                                this.salary=this.salary+addition;
                                }
                                var boss1=new Employee("John", 200000);
                                var boss2=new Employee("Joan", 200000);
                                // add properties to getSalary function object.
                                boss1.getSalary.owner="boss1";
                                boss2.getSalary.owner="boss2";
                                alert(boss1.getSalary.owner);   // prints "boss1"
                                alert(boss2.getSalary.owner);   // prints "boss2"
                                // if both objects are pointing to the same function object, then
                                // both output above should have printed "boss2".
                                // add properties to addSalary function object.
                                boss1.addSalary.owner="boss1";
                                boss1.addSalary.owner="boss2";
                                alert(boss1.addSalary.owner);   // prints "boss2"
                                alert(boss2.addSalary.owner);   // prints "boss2"
                                // since both objects are not pointing to the same function,
                                // then changes in one, affects all instances (so, both prints "boss2"). 

    Run example.

    This might not seem to be a big deal, but there are several consequences of doing nested function like getSalary above: 1) more storage to store the object (because each instance of the object will have its own copy of the getSalary code; and 2) more time required for JavaScript to construct the object.  

    Let's redo the example to make it more efficient.  
    Example DT9

    function Employee(name, salary)
                                {
                                this.name=name;
                                this.salary=salary;
                                this.addSalary=addSalaryFunction;
                                this.getSalary=getSalaryFunction;
                                }
    function getSalaryFunction()
                                {
                                return this.salary;
                                }
                                function addSalaryFunction(addition)
                                {
                                this.salary=this.salary+addition;
                                }

    Here, both functions are pointing to one location, which saves space and construction time (especially if you have a lot of nested functions in the constructor function).  There is another feature of a function that may used to improve this design. It is called "prototype," and we'll talk about it on the next section.

FUNCTION PROTOTYPE

Every constructor function has a property named prototype.  This property is very useful to declare variables or functions that are common to a particular class.
  • You do not need to explicitly declare a prototype property, because it exists on every constructor function.  You can see this below:
    Example PT1
    function Test()
                                {
                                }
                                alert(Test.prototype);  // prints "Object"

    Run example.
     


  • As you've seen above, prototype is an object, therefore, you can add properties to it.  The properties you added to prototype will become common properties of all objects created using the constructor function.  

    For example, below I have a Fish data type.  I want all fishes have these properties:  livesIn="water" and price=20; to accomplish that, I can add those properties to prototype of the constructor function: Fish. 
    Example PT2
    function Fish(name, color)
                                {
                                this.name=name;
                                this.color=color;
                                }
                                Fish.prototype.livesIn="water";
                                Fish.prototype.price=20;

    So let's create some fishes:

    var fish1=new Fish("mackarel", "gray");
                                var fish2=new Fish("goldfish", "orange");
                                var fish3=new Fish("salmon", "white");

    And see what the properties of the fishes are:

    for (int i=1; i<=3; i++)
                                {
                                var fish=eval("fish"+i);   // i'm just getting a pointer to the fish
                                alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price);
                                }

    Run example.
    The output should be: 
    "mackarel, gray, water, 20"
    "goldfish, orange, water, 20"
    "salmon, white  water, 20"

    You see that all fish has the livesIn and price property, even though they are not specifically declared on each individual fish.   This is because when an object is created, the constructor function assigns its prototype property to the internal __proto__ property of the new object.  The __proto__ property is used by the object to look for properties.


  • You can use prototype to assign functions that are common on all objects, too.  This has the benefit of not having to create and initialize the property every time you construct an object.  To illustrate this, let's revisit Example DT9 and rewrite it using prototype like below:
    Example PT3

    function Employee(name, salary)
                                {
                                this.name=name;
                                this.salary=salary;
                                }
    Employee.prototype.getSalary=function getSalaryFunction()
                                {
                                return this.salary;
                                }
                                Employee.prototype.addSalary=function addSalaryFunction(addition)
                                {
                                this.salary=this.salary+addition;
                                }

    We can create the objects as usual

    var boss1=new Employee("Joan", 200000);
                                var boss2=new Employee("Kim", 100000);
                                var boss3=new Employee("Sam", 150000);

    And test it:

    alert(boss1.getSalary());   // prints 200000
                                alert(boss2.getSalary());   // prints 100000
                                alert(boss3.getSalary());   // prints 150000

    Run example.

    Here's an illustration of how prototype works.  Each instance of the object (boss1, boss2, boss3 has an internal property named __proto__, which points to the prototype property of its constructor (Employee).  When you execute either getSalary or addSalary, the object finds it on it's __proto__, and executes the code.  Note that here, there's no code duplication (compare with diagram at Example DT8).

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值