js函數:參數、做用域、匿名函數總結

前言:我入門學的 java這種強類型語言,剛開始學js第一感受是挺簡單,後來發現仍是too young。因此,本次就把做用域、匿名函數作一個完整總結,黑喂狗~~~javascript

-------------------分割線----------------------java

1.函數

返回值/參數

返回值:::::::::::::::::::::::::
js的函數在定義時沒必要指定返回值,並且任何函數均可以經過return隨時返回值。
函數在執行完return以後中止並當即退出,所以return後面的代碼永遠不會執行。數組

function sum(num1,num2){
        return num1 + num2;
        alert("Hello World");  //這段代碼永遠不會執行
    }

參數:::::::::::::::
js函數不介意傳遞進來多少個參數,也不在意傳遞進來的是什麼參數類型。
參數在內部是由一個數組來表示的,函數接收的永遠都是這個數組,函數體內部能夠經過arguments對象來訪問這個參數數組,從而能夠獲取傳遞給函數的每個參數。函數

以下代碼:code

function howManyArgs(){
        alert(arguments.length);
    }
    
    howManyArgs("String",45);  //2
    howManyArgs();             //0
    howManyArgs(12);           //1

從上面的代碼能夠看出來,定義函數時沒有給函數指定參數名字,而調用時依然能夠傳遞進去任何數量和類型的參數,所以在js函數中:命名的參數只是提供便利,但不是必須的。理解這點很重要。對象

不完美的重載:::::::::::::::::
既然能夠用arguments.length判斷傳入參數的個數,那麼js函數也能夠實現不完美的重載。
以下代碼:事件

function doAdd(){
        if(arguments.length == 1){
            alert(arguments[0] + 10);
        }
        if(arguments.length == 2){
            alert(arguments[0] + arguments[1]);
        }
    }
    
    doAdd(10);   //20
    doAdd(30,20);    //50

arguments[i]和對應命名參數的關係:::::::::::::::::
看以下代碼:ip

function doAdd(num1,num2){
        arguments[1] = 10;          //重寫第二個參數
        alert(arguments[0] + num2);
    }

arguments對象中的值會自動反映到對應的命名參數,可是讀取這兩個值不會訪問相同的內存空間,
修改命名參數並不會改變arguments中對應的值
arguments對象長度是由調用函數時傳入的參數個數決定的,不是由定義函數時的命名參數的個數決定的。內存

2.傳遞參數

js中全部的函數都是按值傳遞的。
在向參數傳遞基本類型時,被傳遞的值會被複制給一個局部變量。
在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變化會反映在函數的外部。作用域

來看幾個例子:

function addTen(num){
        num += 10;
        return num;
    }
    var count = 20;
    var result = addTen(count);
    alert(count);       //20,沒有變化
    alert(result);      //30

count傳遞給參數num,函數內部num作了修改,可是沒有反映到count上。

function setName(obj){
        obj.name = "Jack";
    }
    var person = new Object();
    setName(person);
    alert(person.name);     //Jack

這個例子很容易讓人以爲,引用類型作爲函數參數傳遞是按引用傳遞的,由於局部的修改:obj.name = "Jack",反映在了全局的做用域上:person.name,事實上並非如此。
事實上:咱們建立了一個對象,並把它保存在person變量中,而後把person當作參數傳遞到setName()函數中複製給了obj,在這個函數內部obj和person引用的是同一個對象。

再看一個例子:

function setName(obj){
        obj.name = "Jack";
        obj = new Object();
        obj.name = "Rose";
    }
    var person = new Object();
    setName(person);
    alert(person.name);       //Jack

這個例子中在setName()函數中,爲obj從新定義了一個對象,另外一行代碼爲該對象定義了一個帶有不一樣值的name屬性。若是person傳遞給函數setName()以後是按引用傳遞的,那麼對obj.name的修改就會反映到person.name上,事實上person.name依然是Jack。
當在函數內部重寫obj時,這個變量的引用就是一個局部變量了,這個局部變量在函數執行完畢後當即銷燬。

3.執行環境及做用域

定義什麼的太多了就不寫了,直接上代碼

var color = "blue";
    function changeColor(){
        var anotherColor = "red";
        function swapColors(){
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;
            //這裏能夠訪問 color,anotherColor 和tempColor
        }
        
        swapColor();
        //這裏能夠訪問color, anotherColor
    }
    
    changeColor();  //這裏只能訪問color

上面代碼中有三個執行環境:全局環境、changeColor()的局部、和swapColors()的局部環境。
全局環境中有一個color變量和changeColor()函數。
changeColor()的局部環境中有一個名爲anotherColor的變量和一個swapColors()的函數,可是它也能夠訪問全局環境中的變量color
swapColors()局部環境中有一個變量tempColor,只能在這個環境中訪問到。

不管是全局環境仍是changeColor()的局部環境都無權訪問tempColor,在swapColors()內部能夠訪問其餘兩個環境中的全部變量,由於那兩個環境是它的父執行環境。

總結:內部環境能夠經過做用域鏈訪問全部的外部環境,但外部環境不能訪問內部環境中的任何變量和函數,每一個環境內部沒有這個變量或者函數時,均可以向上搜索變量和函數名,直到找到爲止。

沒有塊級做用域

java等語言話括號括起來的代碼塊都有本身的做用域,可是js中總有例外,看下面代碼:

if(true){
        var color = "blue";
    }
    alert(color);    //blue

這裏是在if語句中定義了一個變量color,和做用域函數裏面定義變量的例子不一樣,在if語句中的變量聲明將添加到當前的執行環境中,看下面for循環的例子:

for(var i = 0; i<10; i++){
        doSomething(i);
    }
    alert(i);   //10

對於js,由for語句建立的變量i即便在for循環執行結束後,也依舊會存在於循環外部的執行環境。

4.匿名函數

看下面例子:

window.onload = 
           function(){
               alert("Hello World");
           };
           
       var person = {
           callName: function(){
               alert("My name is Jack");
           }
       };
       person.callName();  
           
       setTimeout(
           function(){alert("Hello World");},
           500);

1.首先,爲load事件建立了一個函數作爲事件處理程序,不會直接調用這個函數,而是在頁面加載時自動調用,因此不必爲這個函數命名,像這樣:

function sayHello(){alert("Hello World");};
    window.onload = sayHello;

2.聲明瞭一個匿名函數,作爲person對象的一個屬性callName,能夠經過該屬性來調用這個方法:
如:person.callName();

3.將匿名函數作爲回調函數傳遞給另一個函數,代碼中將匿名函數作爲一個參數傳遞給window對象的setTimeout()方法,該方法將在半秒後被調用。

-------------------------------------------------------end--------------------------------------------------------

相關文章
相關標籤/搜索