ECMAScript變量分析
ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。
- 基本類型值指的是簡單的數據段,而它包括五種基本數據類型分別是:Undefined、Null、Boolean、Number和String,這五種基本數據類型時按值訪問的,因為操作保存在變量中實際的值。
- 引用類型的值是保存在內存中對象,不能像其他語言一樣,可以直接訪問內存中的位置,也就是說不能直接操作對象的內存空間,而在操作時,實際上是在操作對象的引用而不是實際的對象。所以,引用類型的值是按引用訪問的。
一、變量的動態屬性
基本類型值和引用類型值定義的方式是類似的:創建一個變量並為該變量賦值。
var person = "String";//定義一個基本類型值的變量 var person = new Object();//定義一個引用類型值的變量
但是,要為其不同類型執行的操作是否也類似呢?
我們為基本類型添加屬性或方法時候:
var name = "xiaoming"; name.height = 180; console.log(name.height);//undefined
我們為基本類型變量name定義一個height屬性,並賦值,而接下來在次調用訪問這個屬性的時候,發現屬性不見了。
我們來試試引用類型值的添加屬性或方法:
varperson = new Object(); person.name = "xiaohong"; console.log(person.name);//"xiaoming"
我們構造了一個person的實例,然後為該實例添加了一個name的屬性,並為其賦值上"xiaoming"字符串,最後通過console.log函數輸出這個屬性。所以,很明顯在使用引用類型的值添加屬性和方法的時候,可以成功訪問到屬性的值。
二、復制變量值
基本類型變量的復制過程:
var a1 = 2; var a2 = a1;
在變量對象上創建一個新值,然後把該值復制到新變量分配的位置上,圖解如下所示:
引用類型值的復制
同樣也會將存儲在變量對象中的值復制一份放到為新變量分配空間中,但不同的是,這個值的副本實際上是一個指針,而這個指針指向存儲在堆中的一個對象。復制結束後,前後兩個變量實際上將引用同一個對象。所以說,改變其中一個變量,就會影響另一個變量。
var obj1 = new Object(); var obj2 = obj1; obj1.name = "xiaoming"; console.log(obj1.name); //"xiaoming"
console.log(obj2.name); //"xiaoming"
首先,變量obj1保存了一個對象的新實例。然後,這個值被復制到了obj2中,也就是說,這兩個obj1和obj2都指向同一個對象。這樣obj1添加了name屬性後,就可以通過obj2來訪問這個屬性,因為這兩個變量都引用了同一個對象。
三、傳遞參數
ECMAScript中所以函數的參數都是按值傳遞的。其實,就是遵循上面復制的規則,從一個變量復制到另一個變量中,而基本類型值和引用類型值各自由各自的復制特點。
在向參數傳遞基本類型的值時,被傳遞的參數值會被復制給一個局部變量(即命名參數)。
function add(num) { num += 10; return num; } var count = 20; var result = add(count); console.log(count); //20 console.log(result);//30
全局變量count復制它的值20,給了參數num(局部變量)。而在函數內部,num被加上10,但這並不會影響外部的count變量,這個變量互不相幹,它們有相同的值而已。
那麽,如果使用對象也就是引用類型值傳遞呢,好像不好理解!!!
function myName(obj) { obj.name = "xiaoming"; } var person = new Object(); myName(person); console.log(person.name);//"xiaoming"
上面我們創建了一個對象,並將其保存在了變量person中。然後,這個變量被傳遞到myName()函數中就復制給了obj。那麽,可以說obj和person引用都是同一個對象。也就是說,即使這個變量是按值傳遞的,obj也會按引用來訪問同一個對象。在這裏可以執行添加屬性,在外部也可有所反映。那是因為person指向的對象在堆內存中只有一個,而且全局對象。
然而在函數內部修改對象,在全局作用域會反映出來嗎?
function myName(obj) { obj.name = "xiaoming"; obj = new Object(); obj.name = "huage"; } var person = new Object(); myName(person); console.log(person.name);//"xiaoming"
我們在函數內部重新定義了一個obj對象和修改屬性,但輸出的還是最先那個值,這說明了即使函數內部修改了參數值,但原始的引用仍然保持不變,在重寫的對象中,其實引用的是一個局部變量,而這個局部對象會在函數執行完畢後立即被銷毀。
四、類型檢測
在檢測某個值的對象的時候我們會用typeof,但如果你要檢測它是什麽類型的對象呢?
在這裏ECMAScript提供了instanceof操作符:
result = variable instanceof constructor // (某個對象) (Object、Array....)
如果該變量是給定的引用類型的實例,使用instanceof操作符就可以返回true。
var person = new Object(); var grounp = { a:1, b:2, c:3 }; console.log(person instanceof Object);//"true" console.log(grounp instanceof Object);//"true"
還可以檢測Array、RegExp等。
小總結:
- 基本類型值在內存中占據固定大小的空間,因此被保持在棧內存中;
- 從一個變量向另一個變量復制基本類型的值,會創建這個值的一個副本;
- 引用類型的值是對象,保持在堆內存中;
- 包含引用類型值的變量實際上包含的並不是對象本身,而是一個指向該對象的指針;
- 在一個變量向另一個引用類型的值,復制的其實是指針,因此兩個變量最終都指向同一個對象;
- 基本數據類型可以用typeof操作符檢測,而引用類型可以用instanceof操作符。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果您喜歡或者有所啟發,歡迎添加收藏,一起加油學習啊。
ECMAScript變量分析