1. 程式人生 > >Javascript中資料型別&深淺拷貝二

Javascript中資料型別&深淺拷貝二

資料型別:

Javascript中有5種基本資料型別(簡單資料型別),分別為:Undefined, Null, Boolean, Number和String; 同時還含有一種複雜資料型別,即物件(雖然js中一切皆為物件)

其中Undefined和Null的區別為:

Undefined: 其實是已申明,但是並未賦值的變數,輸出的結構就是undefined
Null:是一個不存在的物件的結果

例如:

var a;
console.log(a);    // undefined

console.log(document.getElementById('node'
)); // 因為沒有id為node的節點,故輸出為null

資料的儲存

* 簡單資料型別的儲存

簡單資料型別的值佔據了記憶體中固定的儲存空間,並被儲存在棧記憶體中;

當複製一個簡單資料型別的值時,會建立這個值的一個副本,該副本會另行開闢一塊棧記憶體空間,改變該副本的值,不會影響原始變數的值。

var a = 'name';
var b = a;    // 將a的值賦給變數b
b = 'age';    // 改變b的值
console.log(a);   // name
console.log(b);   // age;

: 簡單資料型別的變數,不能給其新增屬性

var str
= 'javascript'; str.attr ='cat'; // 給基本資料型別新增屬性 console.log(str.attr); // undefined

* 複雜資料型別的儲存

複雜資料型別即引用資料型別,它的值是物件,儲存在堆記憶體中,包含引用資料型別的變數實際上包含的並不是物件本身,而是一個指向該物件的指標。

所以直接將一個複雜資料型別的變數,賦給另一個變數,實際上只是複製了該變數的指標,因此,兩個變數實際上都指向同一個物件

當修改其中一個變數的值時,原始的變數也會跟著改變。

var obj = {
    name:'dby',
    age: 20
}; var objCopy = obj; // 複製obj給objCopy objCopy.name = 'mxd'; // 改變複製的變數的屬性 console.log(obj); // Object{name:'mxd',age:20} console.log(objCopy); // Object{name:'mxd',age:20}

深淺拷貝

淺拷貝

其實直接賦值就算是一種淺拷貝,對於基本資料型別的簡單賦值,是在棧記憶體中另行開闢了一片儲存空間,對拷貝過來的值進行改變,不會改變原始資料的值。

但是複雜資料型別的值賦值,只是複製了指向該值的指標,當賦值後的值改變時,原始資料的值也會跟隨改變,在實際工程中,這是我們不希望的

var obj = {
    name:'dby',
    age: 20
};
var objCopy = obj;  // 複製obj給objCopy
objCopy.name = 'mxd';  // 改變複製的變數的屬性
console.log(obj);   // Object{name:'mxd',age:20}
console.log(objCopy);   // Object{name:'mxd',age:20}

slice實現陣列的拷貝

const arr = [1,3,4,5,6];
const arr1 = arr.slice(arr);
arr1[1] = 22; // 修改arr1的某個元素
console.log(arr);   // [1,3,4,5,6]
console.log(arr1); // [1,22,4,5,6]

note: 由上面可以看到我們可以使用陣列的原生方法進行陣列的拷貝,但這個拷貝是不是深拷貝呢?,進一步實驗一下:

const arr = [1,3,4,5,[1,3]];
const arr1 = arr.slice(arr);
arr1[4][0] = 222;
console.log(arr);  // [1,3,4,5,[222,3]] 原始陣列被更改了
console.log(arr1);  // [1,3,4,5,[222,3]] 

由這個小實驗可知,slice只能實現一維陣列的深拷貝,不能實現多維陣列的深拷貝

Object.assign()實現物件的拷貝

const obj = {
    x: 1,
    y: 2
};
const obj1 = Object.assign({}, obj);
console.log(obj1);  // { x: 1, y: 2}
obj1.x = 222;
console.log(obj);  // { x: 1, y: 2 }
console.log(obj1); // {x: 222, y: 2 } 實現了物件的深拷貝,沒有改變原物件

const obj2 = {
    x: 1,
    y: {
        m: 2
    }
};
const obj3 = Object.assign({}, obj2);
console.log(obj3); // { x: 1, y: { m: 2 } }
obj3.y.m = 222; // 修改obj3的屬性值
console.log(obj2); // { x: 1, y: { m: 222 } }   // 將原物件也更改了
console.log(obj3); // { x: 1, y: { m: 222 } }

由此可見,Object.assign()方法也只能實現一維物件的深拷貝,不能實現多維物件的深拷貝

深拷貝
陣列
陣列的深拷貝,可是借用陣列的方法slice()和concat()來解決,有侷限性,只能實現一維陣列的深拷貝

slice

var arr = ['cat','dog','mouse'];
var arrCopy = arr.slice(0);   // 返回arr中0-(length-1)長度的陣列,不會改變原陣列
arrCopy[0] = 'tigger';    // 改變arrCopy第一項為tigger
console.log(arr);   // ['cat','dog','mouse']
console.log(arrCopy);      // ['tigger','dog','mouse']

concat

var arr = ['cat','dog','mouse'];
var arrCopy = arr.concat();       // arrCopy = ['cat','dog','mouse'];
arrCopy[0] = 'tigger';            // 改變arrCopy第一項為tigger
console.log(arr);                 // ['cat','dog','mouse']
console.log(arrCopy);            // ['tigger','dog','mouse']

物件
方法一:通過定義一個新的物件,並遍歷原物件,將屬性逐一拷貝上去。

var obj = {
            name:'dby',
            age:20
        };
var objCopy = new Object();
objCopy.name = obj.name;
objCopy.age = obj.age;

objCopy.name = 'chris';
console.log(obj);         // Object{name:'dby',age:20}
console.log(objCopy);       // Object{name:'chris',age:20}

方法二:封裝一個方法來處理物件的深拷貝

var obj = {
            name:'dby',
            age:20
        };
var deepCopy = function(source){     // 物件深拷貝函式
    var result = {};
    for(var key in source){
        if(typeof source[key] === 'object'){    
            // 如果屬性為object型別,則遞迴呼叫深拷貝函式
            result[key] = deepCopy(source[key]);  
        }else{
            result[key] = source[key];
        }
    }
    return result;
}

var objCopy = deepCopy(obj);
objCopy.name = 'chris';

console.log(obj);         // Object{name:'dby',age:20}
console.log(objCopy);       // Object{name:'chris',age:20}