前言:我入門學的 java這種強類型語言,剛開始學js第一感受是挺簡單,後來發現仍是too young。因此,本次就把做用域、匿名函數作一個完整總結,黑喂狗~~~javascript
-------------------分割線----------------------java
返回值:::::::::::::::::::::::::
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對象長度是由調用函數時傳入的參數個數決定的,不是由定義函數時的命名參數的個數決定的。內存
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時,這個變量的引用就是一個局部變量了,這個局部變量在函數執行完畢後當即銷燬。
定義什麼的太多了就不寫了,直接上代碼
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循環執行結束後,也依舊會存在於循環外部的執行環境。
看下面例子:
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--------------------------------------------------------