【javascript基礎】三、變量和做用域

前言

這篇和你們說一下javascript中的變量和做用域,因爲是將基礎嘛,主要給你們捋一下知識,不想翻開書複習的道友能夠看一下,打算剛開始學習javascript的同窗能夠掃一眼。javascript

PS:jQuery源碼交流羣( 239147101)等你來,羣裏高手雲集,讓我受益不淺,儘可能少灌水。java

變量

javascript中有兩種變量,分別是基本類型和引用類型,基本類型是Null,Undefined,String,Boolean,Number這五種,前面簡單的介紹了。引用類型是指Object,Array,Date,RegExp,Function這些。建立這兩種變量是相似的,都是建立一個變量而後給它賦值。不一樣的緣由主要是在內存中位置和操做不一樣。面試

基本類型

基本類型比較簡單,基本類型的值保存在棧中。看例子閉包

var v = 1;

基本類型變量在內存中的表示,沒有涉及堆函數

看看複製變量以後在內存中的表示,執行的代碼以下學習

var v = 1;
var n = v;

解釋一下,若是一個變量把基本類型的值複製給另外一個變量時,會建立一個新的相同的值,把這個新的值賦值給新的變量,這樣,內存中就有兩個同樣的值了,分別是新的變量和就的變量指向的值,雖然值是同樣的。spa

引用類型

應用類型的變量建立和基本類型的建立是同樣的,主要看在內存中的存儲方式,代碼指針

var obj = new Object();

能夠看到,變量obj存儲的是一個地址(我的的理解),其實obj的值是一個指針,指向了堆中的對象,能夠找到堆中的new出來的對象。再看看複製一個變量時的狀況code

var obj = new Object();
var o = obj;

解釋一下,這和基本類型的複製是不一樣的,咱們能夠看出,當一個變量向另外一個變量複製引用類型的值時,也會將存儲在變量對象中的值複製一個給新的變量。複製的這個值實際上就是一個指針,指向堆中的一個對象,因爲指針是相同的,因此知指向的對象就是同一個對象,此時不管你該哪個變量,都會影響另外一個變量,由於指向的對象是用一個嘛,例如對象

var obj = new Object();
var o = obj;
o.name = "hainan";
console.log(obj.name);//"hainan"

而基本類型則不會,看例子

var v = 1;
var n = v;
v = 100;
console.log(n);//1
console.log(v);//100

這個比較簡單,不懂的話看上面的那個內存的圖就懂了。

區別

上面說了複製的那個區別,還有一個就是基本類型不能添加屬性和方法,而引用類型則能夠添加,看例子吧,很簡單

//基本類型
var peo = "hainan";
peo .age = 25;
console.log(peo.age);//undefined,可是不報錯
//引用類型
var people = new Object();
people.age = 25;
console.log(people.age);//25

函數傳參

javascript中函數參數都是按值傳遞的,也就是把函數外部的值複製給函數內部的參數,和複製變量同樣,不管傳遞的是什麼類型,都和複製變量同樣。首先看一下傳遞基本類型的例子

function test(n){
  return n = n+100;  
}
var num = 10;
var result = test(num);
console.log(num);//10 沒有發生變化
console.log(result);//110

簡單解釋一下,調用函數時,傳遞一個基本類型的參數num給函數,此時,複製值給內部的參數n,這樣num和n變量都有了相同的值,可是,這兩個之間沒有任何的關係,只是值相同而已,想一想前面的圖就清楚了,內部的變量也就是參數n改變了以後,num的值並無發生改變。

接下來看看傳遞引用類型的例子

function test(obj){
  obj.name = "hainan";  
}
var people = new Object();
test(people);
console.log(people.name);//"hainan"

解釋一下,其實這個也和複製引用類型變量是同樣的,傳遞一個引用類型給函數參數時,把外部的值複製給內部的函數參數,因爲是引用類型這個值是一個指針,因此外部的引用類型變量和內部的參數此時會指向同一個對象,回想上面複製的圖,當內部的參數指向的對象改變時,外部的變量指向的對象必定會改變,是同一個對象嘛。

因此,如今你只需知道,傳遞參數和複製變量是同一回事,不會的時候回想內存中的存儲方式就明白了,管它是按值仍是按引用傳遞呢,只是一個說法罷了,呵呵,可是面試的時候可能會問,記住javascript是值傳遞就好了。

做用域

執行環境

每一個函數都有本身的執行環境(execution context),執行環境定義了變量和函數有權訪問的數據,太官方了,就是每個函數都有本身能夠訪問的範圍,在本身的範圍內的數據才能夠獲得。每個執行環境中都有一個變量對象,保存着該環境中定義的變量和函數。全局執行環境是最外面的一個執行環境,就是window對象,全部的全局變量和函數就是它的屬性。每個執行環境執行完以後,這個環境就會被銷燬,裏面的變量對象也就沒有了,固然變量對象中的變量和函數也就銷燬了。

做用域

javascript的做用域是指變量和函數能夠訪問的範圍,分爲局部做用域和全局做用域,這個和C語言是相似的,可是不一樣點是javascript的做用域沒有塊級做用域,不像C語言的{}能夠表示一個塊級的做用域,javascript只有函數做用域,在函數內部聲明的變量只能在函數體和子函數能夠訪問,這個函數的外部不能訪問。看例子

//沒有塊級做用域
if(true){
  var n = 1;  
}
console.log(n);//1

//注意
for(var i=0;i<10;++i){
    
}
console.log(i);//10

上面的例子要是在C語言或者java中n和i會在{}語言執行完以後銷燬,在javascript中能夠看到,它們並無銷燬,說明並無塊級做用域。

function test(a,b){
  var sum = a + b;  
  return sum;
}
test(1,2);//3
console.log(sum);//sum is not defined

能夠看出sum是在函數的外部訪問不到的,由於sum函數是在函數的局部做用域中定義的,在函數執行結束時,內部的變量就會銷燬,因此外部的做用域訪問不到它,這就是函數的做用域。

做用域鏈

做用域鏈(scope chain)是保證在執行環境中有序的訪問變量和函數,咱們能夠這樣想,每一個函數都有一個本身的執行環境,這個執行環境的嵌套就像套娃是同樣的,大的套小的,內部的執行環境的變量有權訪問外部的執行環境的數據,而外部的不能夠反問內部的,因此當內外的執行環境中都有一個相同的變量或函數時,你是先訪問哪個呢?因此就有了做用域鏈這個概念。這個做用域鏈的每個節點是一個變量對象,函數中有一個活動變量對象的概念,剛開始時只有函數的arguments對象,以後會把當前的變量對象中的變量和函數複製到活動對象中。做用域鏈的第一個變量對象是活動對象,以後就是下一個包含環境,以後是包含環境的包含環境......直到全局執行環境的變量對象。 看例子可能你們會明白

function test(a,b){
  var  sum = a + b;
  return sum; 
}
var s = test(1,2);

分析上面的這段代碼,在未調用test()函數以前,看看做用域鏈狀況

在未執行函數的時候,能夠看到函數的做用域鏈只用一個全局的變量對象,裏面有window和test函數等,在執行var s = test(1,2);語句的時候,做用域鏈會發生變化,會增長一個函數的活動對象到做用域鏈的前面,這個活動對象的初始值只有函數的arguments對象,以後會把函數的內部變量等複製到這個活動對象中,請看下面的圖

這就是函數的做用域鏈,之後要訪問某個變量的時候,會沿着這個做用域鏈進行查找,即沿着活動對象->外部函數的活動對象->外部函數的外部函數的活動對象->......->終點的全局執行環境,在這期間在某個活動對象中內部有這個值就會返回這個值,這個過程就會中止,不在進入另外一個執行環境中,看個例子

//全局變量
var color = "red";
function getColor(){
    //內部變量
    var color = "blue";
    return color;
}
console.log(getColor());//blue

看到這裏你們會明白了。

PS:其實javascript的閉包和做用域鏈有很大的聯繫,這裏咱不討論,閉包會單獨討論。

小結

這就是javascript的做用域了,主要你們仍是好好看看變量內存那塊,分析幾個就會了。注意一下javascript沒有塊級做用域這一說法,這點你們要當心了。最近幾每天天在羣裏看你們的聊天記錄,論文也不想寫了,哎,坐等回家吃豬肉了,但願老師別鄙視我。

相關文章
相關標籤/搜索