JS基本型別與引用型別知多少

哆來咪er發表於2017-06-30

1、JavaScript值型別和引用型別有哪些

(1)值型別(基本型別):數值(number)、布林值(boolean)、null、undefined、string(在賦值傳遞中會以引用型別的方式來處理)。

(2)引用型別:物件、陣列、函式。

2、如何理解值型別和引用型別

之前看到一個比喻,覺得非常貼切,想要和大家分享一下:

用“連鎖店”和“連鎖店鑰匙”來理解。

(1)值型別:變數的交換等於在一個新的地方按照連鎖店的規範標準(統一店面理解為相同的變數內容)新開一個分店,這樣新開的店與其它舊店互不相關、各自運營。

function str() 
{ 
var str1='Hello World'; 
var str2=str1; 
str1='Hello'; 
alert(str2); //Hello World 
} 
str();複製程式碼

把一個值型別(也叫基本型別)str2賦值給另一個變數時,其實是分配了一塊新的記憶體空間,因此改變str1的值對str2沒有任何影響,因為它不同於引用型別(變數的交換其實是交換了指像同一個內容的地址)。

再看一個例子:

var a = 1;
var b = a;

a ++ ;
console.log(a); // 2
console.log(b); // 1複製程式碼

在從一個變數向另一個變數賦值基本型別時,會在該變數上建立一個新值,然後再把該值複製到為新變數分配的位置上。

例子中,一開始,a中儲存的值為 1 ,當使用 a 來初始化 b 時,b 中儲存的值也為1,但b中的1與a中的是完全獨立的,該值只是a中的值的一個副本,此後,這兩個變數可以參加任何操作而相互不受影響,即基本型別在賦值操作後,兩個變數是相互不受影響的。

這裡寫圖片描述
這裡寫圖片描述

(2)引用型別:變數的交換等同於把現有一間店的鑰匙(變數引用地址)複製一把給了另外一個老闆,此時兩個老闆同時管理一間店,兩個老闆的行為都有可能對一間店的運營造成影響。

function str() 
{ 
var str1=['Hello World']; 
var str2=str1; 
alert(str2[0]); //Hello World 
str1[0]='Hello'; 
alert(str2[0]); //Hello
} 
str();複製程式碼

str2只進行了一次賦值,理論上它的值已定,但後面通過改寫str1的值,發現str2的值也發生了改變,這正是引用型別的特點。

再看個例子:

var a = {}; // a儲存了一個空物件的例項
var b = a;  // a和b都指向了這個空物件

a.name = 'coco';
console.log(a.name); // 'coco'
console.log(b.name); // 'coco'

b.age = 24;
console.log(b.age);// 24
console.log(a.age);// 24

console.log(a == b);// true複製程式碼

這裡寫圖片描述
這裡寫圖片描述

引用型別的賦值其實是物件儲存在棧區地址指標的賦值,因此兩個變數指向同一個物件,任何的操作都會相互影響。

3、值型別和引用型別的區別

(1)基本型別的值是一經確定就不可變的

(2)基本型別的比較是值的比較

只有在它們的值相等的時候它們才相等。

比較的兩個值的型別不同的時候==運算子會進行型別轉換,但是當兩個值的型別相同的時候,即使是==也相當於是===。

var a = 1;
var b = true;
console.log(a == b);//true複製程式碼

在用==比較兩個不同型別的變數時會進行一些型別轉換。如上的比較先會把true轉換為數字1再和數字1進行比較,結果就是true了。

var a = 'coco';
var b = 'coco';
console.log(a === b);//true複製程式碼

(3)基本型別的變數是存放在棧區的(棧區指記憶體裡的棧記憶體)

var name = 'coco';
var city = 'shenzhen';
var age = 24;複製程式碼
儲存結構如下:

這裡寫圖片描述
這裡寫圖片描述

棧區包括了 變數的識別符號和變數的值。

(4)引用型別的值是可變的

可以為引用型別新增屬性和方法,也可以刪除其屬性和方法。

var person = {};//建立一個空物件 --引用型別
person.name = 'coco';
person.age = 24;
person.sayName = function(){
console.log(person.name);
} 
person.sayName();// 'coco'複製程式碼
var person = {};//建立一個空物件 --引用型別
person.name = 'coco';
person.age = 24;
person.sayName = function(){
console.log(person.name);
} 
person.sayName();// 'coco'
delete person.name; //刪除person物件的name屬性
person.sayName(); // undefined複製程式碼

引用型別可以擁有屬性和方法,並且是可以動態改變的。

(5)引用型別的值是同時儲存在棧記憶體和堆記憶體中的物件

js不同於其他語言,其不允許直接訪問記憶體中的位置,也就是說不能直接操作物件的記憶體空間,實際上,是操作物件的引用,所以引用型別的值是按引用訪問的。

準確地說,引用型別的儲存需要記憶體的棧區和堆區(堆區是指記憶體裡的堆記憶體)共同完成,棧區記憶體儲存變數識別符號和指向堆記憶體中該物件的指標,也可以說是該物件在堆記憶體的地址。

var person1 = {name:'zhangsan'};
var person2 = {name:'lisi'};
var person3 = {name:'wangwu'};複製程式碼

則這三個物件的在記憶體中儲存的情況如下圖:

這裡寫圖片描述
這裡寫圖片描述

(6)引用型別的比較是引用的比較

var person1 = '{}';
var person2 = '{}';
console.log(person1 == person2); // true複製程式碼

基本型別的比較--當兩個比較值的型別相同的時候,相當於是用 === ,所以輸出是true。

var person1 = {};
var person2 = {};
console.log(person1 == person2); // false複製程式碼

上面比較的是兩個字串,而下面比較的是兩個物件,為什麼長的一模一樣的物件就不相等了呢?

引用型別是按引用訪問,換句話說就是比較兩個物件的堆記憶體中的地址是否相同,那很明顯,person1和person2在堆記憶體中地址是不同的:

這裡寫圖片描述
這裡寫圖片描述

這兩個是完全不同的物件,所以返回false。

相關文章