1.簡單數據類型
在JavaScript中簡單數據類型分為5種。分別為 Undefined, Null,Boolean,Number,String.
- Undefined類型
Undefined類型只有一個值,即特殊的undefined。在使用var對變量聲明的時候,變量的值即被初始化為undefined.
在使用typeof求得數據類型的時候,對於未聲明的變量返回的總是undefined. - Null類型
Null也只有一個值得數據類型,其實質是一個指向空對象的指針。所以使用typeof操作的時候返回的是一個object類型。var object = null; console.log(typeof object);//object
- Boolean類型
Boolean類型只有兩個值ture或者false. - Number類型
Number數據類型有幾個比較常使用的數值常量。
(1)最大值: Number.MAX_VALUE.
(2)最小值: NUMBER.MIN_VALUE.
(3)正無窮大: Infinity
(4)負無窮大: -Infinity
(5)非數字 :NaN.
數值轉換:
數值轉換有三種常見的函數:Number, ParseInt, ParseFloat
Number常用於轉型函數,而后兩種常用於字符串轉數值。 - String類型
基本數據類型中,最復雜的一種也就是字符串了。
字符串的特點:
(1).單雙引號報過都可。
(2).字符串不可變。
什么是字符串不可變?
var str1="hello"; //開辟一份新空間 var str2="world"; //開辟一份新空間 str1=str1+str2; //因為字符串的不可變特性,改變字符串會重新開辟一份新空間,以前的那一份在一段時間后被垃圾回收機制回收。 console.log(str1); 作為基本數據類型,字符串作為函數參數的時候是傳值還是傳引用呢?
如果傳值: 則必須復制一份新的字符串作為參數。字符串如果太長的話,很明顯,浪費空間。
如果傳引用: 字符串作為參數,如果值沒有改變,則不需要開辟新的空間。
如果值改變,又因為字符串不可變的特性而會重新開辟新的空間。
所以,字符串在作為參數的時候是傳引用而非傳值。
注: 關於傳值還是傳引用,我們這里參照物均為變量本身。------詳情請看第三部分。
2.復雜數據類型
復雜數據類型也就是我們常說的引用類型.引用類型是一種數據結構,其值也就是其所指向的對象。
ECMAScript提供了很多原生態的引用類型。
- Object類型
(1)兩種創建方式
-
-
//第一種,使用構造函數。 var object=new Object(); object.name="xxxx"; //第二種,使用字面量的創建方式,程序員更傾向於第二種 var object={ name:"xxxx" }
-
(2) 對象的屬性
Object數據類型的對象是采用松散的結構組織屬性的。所謂的松散,就是我們所說的用鍵值對來保存對象。而鍵值對以hash表結構存儲。
有關於對象以鍵值對存儲的文章,詳情見對象基於哈希存儲(<Key,Value>之Key篇(1)。
Object類型的對象,屬性可以分為兩類:
a.數據屬性
數據屬性有四個描素其行為的特性。
-
-
- [Configurable]:表示是否可以通過delete刪除該屬性,能否修改屬性的特性。
var object = { name: '張三' }; Object.defineProperty(object, 'name', { configurable: false }); delete object.name; //false
Object.defineProperty(object, 'name', {
configurable: true
}); //error:TypeError: can't redefine non-configurable property 'name' - [Enumerale]:表示該屬性是否可以通過for-in循環遍歷
var object = { name: '張三', age:18 }; Object.defineProperty(object, 'name', { configurable: false, enumerable: false }); for (var property in object) { console.log(property); //age }
- [Writable]:表示該屬性的值是否能被更改
var object = { name: '張三', age: 18 }; Object.defineProperty(object, 'name', { configurable: false, enumerable: false, writable: false }); object.name = '李四'; console.log(object.name);//張三,name未被更改
- [Value]:保存屬性的值,寫入屬性的時候,將屬性的值保存在此位置;讀取此屬性的時候,從該值中讀取。
var object = { name: '張三' }; Object.defineProperty(object, 'age', { configurable: false, enumerable: false, writable: false, value: 18 }); console.log(object.age);//18
- [Configurable]:表示是否可以通過delete刪除該屬性,能否修改屬性的特性。
-
b.訪問器屬性:訪問器屬性不包含數據值,但其包含getter和setter函數。其也有四個特征值對其進行描述
-
- [Configurable]:表示是否可以通過delete刪除該屬性,能否修改屬性的特性。
- [Enumerale]:表示該屬性是否可以通過for-in循環遍歷。
- [Get]:在讀取屬性時,調用的函數。
var object = { name: '張三', age: 18 }; Object.defineProperty(object, 'isAdult', { configurable: false, enumerable: false, get: function () { if (this.age < 18) { return false; } else { return true; } } }); console.log(object.isAdult);//true isAdult就是一個訪問器屬性
- [Set]:在寫入屬性時調用的函數
var object = { name: '張三', age: 18 }; Object.defineProperty(object, 'changeAge', { configurable: false, enumerable: false, set: function (value) { this.age = value; } }); object.changeAge=17; console.log(object.age);//17
(3)對象的常用方法
- toString():返回對象的字符串形式。
- valuOf():該方法取得對象的基本數據類型。一般返回的是個數值。 其與toString()方法的區別,詳請見JavaScript系列-----對象基於哈希存儲(<Key,Value>之Value篇) (3)。
- hasOwnProperty(attrName):判斷對象中是否有某一個屬性,且該屬性不在對象的原型對象上。
- Object.getOwnPropertyNames(object):返回一個數組,數組中保存着對象object中的所有屬性名,且該屬性不在原型對象上。
- Array類型
(1)定義:數組對象用來在單獨的變量名中存儲一系列的值。
(2)創建方式:
//第一種 使用構造函數 var mycars=new Array() mycars[0]="Saab" mycars[1]="Volvo" mycars[2]="BMW" 或者 var mycars=new Array("Saab","Volvo","BMW") //第二種 使用數組字面量 var colors=["red","blue","green"];
length:數組的 length 屬性總是比數組中定義的最后一個元素的下標大 1,通過length 屬性可設置或返回數組中元素的數目。
var array=[]; array[100]=10; console.log(array.length);//101
array=[1,2,3,4,5];
array.length=3;
console.log(array);//1,2,3
- Date類型
創建方式:
-
var myDate=new Date(); //Date 對象自動使用當前的日期和時間作為其初始值。
-
- 函數類型(參見本系列第四篇函數篇)
- RegExp類型(在下一篇文章中作為專題)
3.傳值和傳引用
(1)問題的起源
其實這個問題來源於C和C++,因為C或C++里都有一個特殊的數據類型----指針,那時候所謂的傳值和傳引用是對指針來說的。
那么針對指針來說,什么是傳值,又什么是傳引用呢?首先,指針作為一種數據類型,其本身肯定是占用一定的內存空間,而且,指針同時還要指向另一塊內存空間。
如圖所示:
針對指針來說,指針作為一種數據類型,指針的標識符也就是其在內存中所在的地址,指針的值就是其所指向的地址。也就是說,指針值是地址。
那么一個指針在作為函數的參數的時候,傳給參數的到底是指針的地址還是指針的值呢? ----這就是傳值和傳引用問題的起源.
(2)指針的傳值和傳引用
函數的參數,在函數被調用的時候,在其函數所開辟的那個棧中是占用一定的內存空間的,那么這塊內存空間的值是什么呢?
首先,先肯定一點,形參的值來源肯定是實參。那么,實參給形參賦值的時候傳的是什么呢?
(1)傳的是結構體地址,那么就如圖所示:
如果傳的是結構體的地址,那么也就是指針的值賦值給形參。所以,這就是我們所謂的傳值。由圖中我們可以看出,如果是傳值的話,那么作為形參的指針和作為實參的指針,指向的是同一個結構體。
(2)如果傳的是指針的地址,那么指針的地址就會賦值給形參。結果,如圖所示:
對於傳引用來說,形參的值是指針的地址,那么每次對形參的改變,其實改變的都是指針所指向的地址。而如果向取得結構體的地址,也需要通過實參的地址。
(3)JS中只存在傳值
首先需要明確一寫概念,即引用和對象的概念。
引用:也就是我們所說的指針,其存在於棧中。
對象:也就是上圖中我們所標記的結構體,其存在於堆中。
引用的值就是對象所在的地址。我們用一個例子說明:
var person = { name: '張三', age: 18 }; function setName(object) { object.name = '王五'; } setName(person); console.log(person.name);//王五
依然用圖來解釋:
從上圖中我們可以看書,函數在傳參是,傳的是實參的值,也就是指針的值。我們,我們這里理解為傳值。
但是,換一種角度來說,把對象當作參照物,傳給形參的值就是對象的地址,也並非對象本身,說是傳引用也不為過。
所以,無論是傳值還是傳引用,只要理解了即可。沒必要糾結於字眼。
(4)基本數據類型傳值
首先基本數據類型與引用數據類型相比的差別就是,基本數據類型保存在函數運行時的棧中,而引用數據類型的值保存在堆空間中。那么,如果基本數據類型給形參賦值的時候是怎樣的呢?
舉個例子:
var x = 1; function add(num) { return num + 1; } add(1);
x作為實參,num作為函數的形參,當函數add被調用時,形參num被賦值為實參x的值。即 num=1;結構圖如下所示:
如上如所示,此時對形參的值做任何改變均與實參無關,實參和形參是兩個互不關聯的個體。
補充一點: 字符串也是一種數據類型,那么字符串的值保存在哪里呢? 其作為函數參數的時候,傳值是如何進行的呢?
字符串雖然也是一種基本數據類型,但因為其大小不固定,所以,其一般其更像與引用數據類型。但其又有其特殊性----不可變性。
什么是字符串的不可變?
var str=""; for(var i=0;i<3;i++){ str+=i; } console.log(str);//012
看上圖程序,字符串是可以改變的。那為什么還要說,字符串不可變呢,先別急,我們來分析一下程序運行時內存結構圖。
看到沒有,每一次字符串值的改變,其所指向的地址都會跟着改變一次。其所說的不可變,是跟引用數據類型相比來說,引用數據類型值的改變一般是對象本身的改變,而其指向是不變的,而字符串值的改變是其指向地址的改變。所以,字符串的每一次改變都會產生垃圾,此垃圾過一段時間會被垃圾回收機制回收。
了解了上述的情況,所以字符傳在作為參數的時候,為了節省空間,只是復制了字符串所指向的地址給形參,而形參的值如果改變了的話,因為字符串具有不可變特性,所以會重新開辟一份空間給形參。如下
var str = '你是誰?'; function change(str) { str = '我就是我了'; } change(str); console.log(str);//你是誰
字符串在Java C++中均作為比較特殊的一種類型,JS中雖然將其當作一種基本數據類型,但是其使用時更偏向於引用數據類型,但是其又具有不可變型,才促使我們可以將其當作基本數據類型使用。