1. 程式人生 > >Javascript二(函數詳解)

Javascript二(函數詳解)

弱類型語言 9.1 指定 fun 並不會 val 避免 中國 width

一.函數

Javascript是一門基於對象的腳本語言,代碼復用的單位是函數,但它的函數比結構化程序設計語言的函數功能更豐富。JavaScript語言中的函數是“一等公民”,它可以獨立存在;而且JavaScript的函數完全可以作為一個類來使用(而且它還是該類唯一的構造器);與此同時,函數本身也是一個對象,函數本身是function

實例。

函數的最大作用是提供代碼復用,將需要重復使用的代碼塊定義成函數,提供更好的代碼復用。函數可以有返回值,可以沒有返回值

1.定義函數的三種方式

a)定義命名函數,

語法格式如下:

function functionName(param1,param1,...){

staments;
}

b)定義匿名函數

語法格式如下:

function(parameter list){
staments
};

與命名函數的區別是沒有函數名,函數後面有個分號。

當通過這種語法格式定義了函數之後,實際上就定義了一個函數對象(即function實例),接下來可以將這個對象賦給另外一個變量。例如下面代碼:

<script type=‘text/javascript‘>
var f = function(name)
{
  document.writeln(‘匿名函數<br/>‘);
 document.writeln(‘你好‘+name);
}
f(‘yukey‘);
</script>

   使用匿名函數提供更好的可讀性。

c)使用function類匿名函數

JavaScript提供了一個function類,該類也可以用於定義函數,Function類的構造器的參數個數可以不受限制,function可以接受一系列的字符串參數,其中

最後一個字符串參數是函數的執行體,執行體的各語句以分號(;)隔開,而前面的各字符串參數則是函數的參數,看下面定義函數的方式:

<script type=‘text/javascript‘>
var f = new Function(‘name‘,"document.writeln(‘Function定義的函數<br/>‘);"
          +"document.writeln(‘你好‘+name);");

  f(‘yukey‘);
</script>

2.局部函數

局部函數在函數裏定義,看下面代碼

 <script type="text/javascript">
       function outer(){
           function inner1(){
               document.write("局部函數11111<br/>");
           }

           function inner2(){
               document.write("局部函數22222<br/>");
           }
           inner1();
           inner2();
           document.write("結束測試局部函數<br/>");
       }
       outer();
       document.write("調用outer之後...");
   </script>

在外部函數裏調用局部函數並不能讓局部函數獲得執行的機會。只有當外部函數被調用時,外部函數裏調用的局部函數才獲得執行的機會。

3.數,方法,對象,變量和類 函數是JavaScript的“一等公民”,函數是JavaScript變成裏非常重要的一個概念,當使用JavaScript定義了一個函數之後,實際上可以得到如下四項。

函數:就像Java的方法一樣,這個函數可以被調。

對象:定義一個函數時,系統也會創建一個對象,該對象時Function類的實例

方法:定義一個函數時,該函數通常會附加給某個對象,作為該對象的方法

變量:在定義一個函數的同時,也會得到一個變量

:在定義函數的同時,也得到一個與函數同名的類

定義函數之後,有如下兩種方式調用函數

直接調用函數:直接調用函數總是返回該函數體內最後一條return語句的返回值;如果該函數體內不包含return語句,則直接調用函數沒有返回值。

使用new關鍵字直接調用函數:通過這種方式調用總有返回值,返回值就是一個Javascript對象。

<script type="text/javascript">
        var test = function(name)
        {
            return "你好,"+name;
        }
        var rval = test(Sherman);
        var obj = new test("Sherman");
        alert( rval+"\n"+obj);
    </script>

可以看出,第一種方式直接調用函數,返回的是return語句返回值,第二種使用new關鍵字調用給函數,也就是將函數當成類來使用,得到的是一個對象。

下面程序定義了一個person函數,也就定義了一個person類,該person函數也會作為Person類唯一的一個構造器,定義了person函數時希望為該函數定義

了一個方法。

<script type="text/javascript">
        function Person(name ,age){
            this.name = name;
            this.age = age;
            this.info=function(){
                document.writeln("我的名字是"+this.name+<br/>);
                document.writeln("我的年紀是"+this.age+<br/>);
            }
        }
       var p = new Person(Sherman,24);
        p.info();
    </script>

被this關鍵字修飾的的變量不再是局部變量,它是該函數的實例屬性。

JavaScript定義的函數可以“附加”到某個對象上,作為該對象的方法。實際上如果沒有明確指定將函數“附加”到哪個對象上,該函數默認“附加”到window

對象上,作為window對象的方法。

例如如下代碼:

<script type="text/javascript">
        function hello(name)
        {
            document.write(name+",您好<br/>")
        }
        window.hello("孫大聖");
         p = {
        //定義一個函數,該函數屬於p對象
            walk:function(){
                for(let i = 0 ; i < 2  ; i++){
                    document.write("慢慢地走...<br/>");

                }
            }
        };
        p.walk();
    </script>

4.函數的實例屬性和類屬性

由於JavaScript函數不僅僅是一個函數,而且是一個類,該函數還是此類唯一的構造器,只要在調用函數時使用new關鍵字,就可返回一個object,這個object

不是函數的返回值,而是函數本身產生的對象。因此JavaScript中定義的變臉不僅有局部變量,還有實例屬性和類屬性兩種。根據函數中聲明變量的方式,

中的變量有三種

a)局部變量:在函數中以var聲明的變量

b)實例屬性:在函數中以this前綴修飾的變量

c)類屬性:在函數中以函數名前綴修飾的變量

局量只能在函數裏訪問的變量。實例屬性和類屬性是面向對象的概念:實例屬性是屬於單個對象的,因此必須通過對象來訪問,類屬性是屬於整個類本身

(也就是函數)的,因此必須通過類來訪問。

同一個類只占用一塊內存,因此每個類屬性只占用一塊內存;同一個類每創建一個對象,系統將為該對象的實例屬性分配一塊內存。

 <script type="text/javascript">
        function Person(national,age){
            this.age = age;
            Person.national = national;
            var bb = 0;
        }
        var p1 = new Person(‘中國‘,29);
        with(document){
            writeln("創建第一個Person對象");
            writeln("p1的age屬性為:"+p1.age+"<br/>");
            writeln("p1的national屬性為:"+p1.national+"<br/>");
            writeln("通過Person訪問靜態national屬性為:"+Person.national+"<br/>");
            writeln("p1的bb屬性為"+p1.bb+"<br/><hr/>");
        }

         var p2 = new Person(‘美國‘,32);
        with(document){
            writeln("創建兩個Person對象中後<br/>");
            writeln("p1的age屬性為:"+p1.age+"<br/>");
            writeln("p1的national屬性為:"+p1.national+"<br/>");

            writeln("p2的age屬性為:"+p2.age+"<br/>");
            writeln("p2的national屬性為:"+p2.national+"<br/>");
            writeln("通過Person訪問靜態national屬性為:"+Person.national+"<br/>");
            
        }
    </script>

瀏覽器輸出:

創建第一個Person對象 p1的age屬性為:29
p1的national屬性為:undefined
通過Person訪問靜態national屬性為:中國
p1的bb屬性為undefined


創建兩個Person對象中後
p1的age屬性為:29
p1的national屬性為:undefined
p2的age屬性為:32
p2的national屬性為:undefined
通過Person訪問靜態national屬性為:美國

值得指出的是,JavaSript和java不一樣,它是一種動態語言,它允許隨時為對象增加屬性和方法,當直接為對象的某個屬性賦值時,即可視為給對象增加屬性

5.調用函數的3種方式

5.1 直接調用函數

如下代碼:

//調用window對象的alert方法
window.alert();
//調用p對象的walk方法
p.walk();

當程序使用window對象調用方法時,window調用者可以省略

5.2 以call方式調用函數

直接調用函數的方式簡單易用,但這種調用方式不夠靈活,有時候調用函數時需要動態的傳入一個函數引用,此時為了動態地調用函數,就需要call方法。call調用函數

語法格式為

函數引用.call(調用者,參數1,參數2,...)

下面通過call方法調用each函數:

<script type="text/javascript">
        var each = function(array,fn){
            for(var index in array){
                fn.call(null,index,array[index]);
            }
        }
        each([4,20,3],function(index,ele){
            document.writeln("第"+index+"個元素是:"+ele+"<br/>");
        });
    </script>

瀏覽器輸出:

第0個元素是:4
第1個元素是:20
第2個元素是:3

5.3 以apply()方法調用函數

apply()方法和call()方法比較類似,都可以動態的調用函數,他們的區別是:

a)通過call()方法調用函數時,必須在括號中列出每個參數

b)通過apply()動態地調用函數時,需要以數組形式一次性傳入所有調用函數

以下代碼示範了call()和apply()的關系

<script type="text/javascript">
        var myfun = function(a,b){
            alert(‘a的值是‘+a+‘\nb的值是‘+b);
        }
        //以call()方式動態的調用函數
        myfun.call(window,5,20);
        //以apply()方式動態的調用函數
        myfun.apply(window,[3,12]);
        var example = function(num1,num2){
            //直接用arguments代表調用example函數時傳入的所有函數
            myfun.apply(this,arguments);
        }
        example(20,40);
    </script>

由此可見,apply()和call()對應關系如下:

函數引用.call(調用者,參數1,參數2,...); = 函數引用.apply(調用者,[參數1,參數2,...]);

6.函數獨立性

雖然定義函數時可以將函數定義成某個類的方法,或定義成某個對象的方法。但JavaScript的函數是“一等公民”,他永遠是獨立的,函數永遠不會從屬於其他類,對象。

下面代碼示範了函數的獨立性:

<script type="text/javascript">
        function Person(name){
            this.name = name;
            this.info = function (){
                alert("我的name是:"+this.name);
            }
        }

        var p = new Person("Sherman");
        //調用p對象的info方法
        p.info();

        var name = "測試名稱";
       //以window對象作為調用者來調用p對象的info方法
        p.info.call(window);
    </script>

技術分享圖片

當使用匿名內嵌函數定義某個類的方法是時,該內嵌函數一樣是獨立存在的,該函數也不是作為該類實例的附庸存在,這些內嵌函數也可以被分離出來獨立使用,成為另一個對象的函數。如下代碼再次證明函數的獨立性:

<script type="text/javascript">
        function Dog(name,age,bark)
        {
            this.name = name;
            this.age = age;
            this.bark = bark;
            //使用內嵌函數為Dog實例定義方法
            this.info = function(){
                return this.name+"的年齡為:"+this.age+",它的叫聲為:"+this.bark;
            }
        }

        var dog = new Dog("旺財",3,"汪汪,汪汪...");
        function Cat(name,age){
            this.name = name;
            this.age = age;
        }
     //將dog實例的info方法分離出來,在通過call方法調用info方法
    //此時cat為調用者
        var cat = new Cat("Kitty",2)
        alert(dog.info.call(cat));
    </script>

技術分享圖片

7.函數提升

JavaScript允許先調用函數,然後再在後面定義函數,這就是典型的函數提升:JavaScript會將全局函數提升到根元素<script.../>元素的頂部定義

例如如下代碼:

 <script type="text/javascript">
        console.log(add(2,5));
        function add(a,b){
            console.log("執行add函數")
            return a+b;
        }
    </script>

和下面代碼效果是一樣的:

 <script type="text/javascript">
        function add(a,b){
            console.log("執行add函數")
            return a+b;
        }
        console.log(add(2,5));
    </script>

效果如圖:技術分享圖片

如果使用程序先定義匿名函數,然後將匿名函數賦值給變量,在這種方式下依然會發生函數提升,但此時只提升被賦值的變量,函數定義本省不提升。例如

 <script type="text/javascript">
        console.log(add(2,5));

        var add = function(){
            console.log("執行add函數");
            return a+b;
        }
    </script>

效果如圖:技術分享圖片

局部函數會被提升到所在函數的頂部,如

 <script type="text/javascript">
        function test() {
            function add(a, b) {
                console.log("執行add函數")
                return a + b;
            }

            console.log(add(2, 5));
        }
        test();
    </script>

JavaScript編程時應盡量避免變量名和函數名同名。否則會發生覆蓋的情形:分兩種情況

a)定義變量時只用var定義變量,不分配初始值,此時函數的優先值更高,函數會覆蓋變量。

b)定義變量時為變量值指定了初始值,此時變量的優先值更高,變量會覆蓋函數

測試代碼如下:

<script type="text/javascript">
        function a(){}
        var a;
        console.log(a);
        var b;
        function b(){}
        console.log(b);
        var c = 1;
        function c(){};
        console.log(c);

        function d(){}
        var d = 1;
        console.log(d);
    </script>

效果如下:

技術分享圖片

8.箭頭函數

箭頭函數相當於其他語言的Lambda表達式或閉包語法,箭頭函數是普通函數的簡化寫法。語法格式如下:

(param1,param2,param3,...) => {staments}

相當於定義了如下函數:

function(param1,param2,param3,...){}

如果箭頭函數的執行體只有一條return語句,則允許省略函數執行體的花括號和return關鍵字。

如果箭頭函數的形參只有一個參數,則允許省略形參列表的圓括號。

如果箭頭函數沒有形參,則圓括號不可以省略。

(param1,param2,param3,...) => expression
//等同於(param1,param2,param3,...) =>{return expression}
singleParam => {staments}
//等同於(singleParam) => {staments}

下面代碼示範了箭頭函數代替傳統函數:

<script type="text/javascript">
        var arr = ["yuekey","fkit","leegang","sczit"];
        var newArr1 = arr.map(function(ele){
            return ele.length;
        });
        var newArr2 = arr.map((ele) =>{return ele.length});
        var newArr3 = arr.map(ele => ele.length);
        console.log(newArr3);
        arr.forEach(function(ele){
            console.log(ele);
        });
        arr.forEach((ele) => {console.log(ele);})
        arr.forEach(ele => console.log(ele));

    </script>

與普通函數不同的是,箭頭函數並不擁有自己的this關鍵字,對於普通函數而言,如果程序通過new調用函數創建對象,那麽該函數中的this代表所創建的對象;

如果直接調用普通函數,那麽該函數的this代表全局對象(window)。例如,如下代碼示範了this關鍵字的功能。

<script type="text/javascript">
        function Person(){
            this.age = 0;//Person()作為構造器使用時,this代表構造器創建的對象
            setInterval(function growUp(){
                console.log (this=== window);//對於普通函數來說,this代表全局對象window,總是返回true
                this.age++;
            },1000);
        }
        var p = new Person();
        setInterval(function(){
            console.log(p.age);//此處訪問p對象的age,總是0
        },1000);
    </script>

箭頭函數中的this總是代表包含箭頭函數的上下文,例如:

<script type="text/javascript">
        function Person(){
            this.age = 0;
            setInterval(() => 
            {
                console.log(this === window);
                this.age++;//this總是代表包含箭頭函數的上下文
                },1000);
        }

        var p = new Person();
        setInterval(() => console.log(p.age),1000);//此處訪問的是p對象的age,總是不斷加1

    </script>

技術分享圖片

如果在全局範圍內定義箭頭函數,那麽箭頭函數的上下文就是window本身,this代表全局對象window對象。

<script type="text/javascript">
var f = () => {return this;};
console.log(f() === window);//輸出true

</script>

箭頭函數並不綁定arguments,因此不能在箭頭函數中通過arguments來訪問調用箭頭函數的參數,箭頭函數的arguments總是引用當前上下文的arguments。例如

<script type="text/javascript">
    var arguments = "Sherman";
    var arr = () => arguments;
    console.log(arr());

    function foo(){
      var f = (i) => ‘Hello,‘+arguments[1];
      return f(2);
    }
    console.log(foo("Sherman","Leegang"));//箭頭函數中的arguments引用當前上下文的arguments,此時代表調用foo函數的參數
  </script>

9.函數的參數處理

大部分時候,函數都需要接受參數傳遞。與Java完全類似,JavaScript的參數傳遞也全部采用值傳遞方式。

9.1基本類型和復合類型的參數傳遞

對於基本類型參數,JavaScript采用值傳遞方式,當通過實參調用函數時,傳入函數裏的並不是實參本身,而是實參的副本,因此在函數中修改參數值並不會對實參

有任何影響:

 <script type="text/javascript">
    function change(arg1) {
      arg1 = 10;
      document.writeln("函數執行中arg1的值為:" + arg1 + "<br/>");
    }
    var x = 5;
       document.writeln("函數調用前x的值為"+x+"<br/>");
       change(x);
       document.writeln("函數調用之後的x值為"+x+"<br/>");

  </script>

技術分享圖片

對於復合類型的參數,實際上采用的依然是值傳遞方式,只是很容易混淆。看如下程序

<script type="text/javascript">
    function changeAge(person)
    {
      person.age = 10;
      document.writeln("函數執行中age的值為:" + person.age + "<br/>");
      person = null;
    }
    var person = {age:5};
    document.writeln("函數調用之前age的值為"+person.age+"<br/>");
    changeAge(person);
    document.writeln("函數調用之後的age值為"+person.age+"<br/>");
    document.writeln("person對象為"+person);
  </script>

技術分享圖片

9.2空參數

在JavaScript中,在函數聲明時包含了參數,但調用時沒有傳入實參,這種情況是允許的,JavaScript會自動將參數值設置為undefined值,對於JavaScript來說,函數名就是函數的唯一標識。

如果先後定義兩個同名,形參列表不同的函數,這不是函數重載,這種情況後面定義的函數會覆蓋前面的函數。

9.3參數類型

JavaScript是弱類型語言,參數列表無需聲明參數類型。

“鴨子類型”的理論認為,弱類型語言的函數需要接收參數時,則應先判斷參數類型,判斷參數是否包含了需要訪問的屬性、方法。當條件都滿足時 ,程序才會真正

執行。看如下代碼

<script type="text/javascript">
    function changeAge(person) {
      if (typeof person == ‘object‘ && typeof person.age == ‘number‘) {
        document.writeln("函數調用之前age的值為" + person.age + "<br/>");
        person.age = 10;
        document.writeln("函數執行中age的值為:" + person.age + "<br/>");
      }
      else {
        document.writeln("參數類型不符合" + typeof person + "<br/>")
      }
    }
      changeAge();
      changeAge("Sherman");
      changeAge(true);

      p = {abc:34};//json格式創建第一個對象
      changeAge(p);

      person = {age:25};//json格式創建第二個對象
      changeAge(person);
  </script>

技術分享圖片

Javascript二(函數詳解)