JavaScript資料型別及深拷貝與淺拷貝
JavaScript的資料型別分為基本型別和引用型別。
基本型別
基本型別包括undefined、null、string、number、boolean、(ES6中還有Symbol)
這些型別是存放在棧中的,資料大小確定,可按值訪問,直接操作儲存在變數中的實際值。
var a = 10;
var b = a;
b = 20;
console.log(a); // 10
console.log(b); // 20
複製時棧記憶體會開闢一個新記憶體來存放。按值傳遞,改變其中一個不會改變另一個,互相獨立。
引用型別
引用型別就是Object(包括Array、RegExp、Date、function)
引用型別是存放在堆記憶體中的物件。
變數是儲存在棧記憶體中的一個指標,這個指標儲存的是堆記憶體中的引用地址,指向堆記憶體中的物件。
var obj1 = new Object();
var obj2 = obj1;
obj2.name = "名字";
console.log(obj1.name); //名字
按址傳遞,=只是將這個堆記憶體物件在棧記憶體中的地址複製了一份給obj2,但是它們指向了同一個堆記憶體物件,修改其中一個變數就是在修改另一個。
判斷資料型別的方法
typeof
typeof可以得到以下幾種型別:
number、string、boolean、object、function、undefined、symbol
typeof ''; // string 有效
typeof 1; // number 有效
typeof Symbol(); // symbol 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 無效
typeof [] ; //object 無效
typeof new Function(); // function 有效
typeof new Date(); //object 無效
typeof new RegExp(); //object 無效
需要注意的是
typeof null; //object
而且除了function外無法具體區分其他的object
instanceof
用來判斷A是否是B的例項,返回的是一個布林值。
console.log([] instanceof Array); //true
console.log([] instanceof Object); //true
console.log(/^$/ instanceof RegExp); //true
需要注意的是
這種方式判斷一個數組是否為陣列或物件的結果都為true,所以可以用Array.isArray()來判斷Array型別。
這種方式不可以用來檢測基本型別
console.log(1 instanceof Number); //false
constructor
物件的constructor是指向其建構函式的。
console.log([].constructor === Array); //true
console.log([].constructor === Object); //false
console.log({}.constructor === Object); //true
console.log([].constructor === Object); //false
console.log(1.constructor === Number); //true
需要注意的是
null和undefined是沒有constructor的,不能用這種方式判斷。
toString()
這種方法是最萬能的。
toString()是Object的原型方法,會返回當前物件的資料型別。通過call()使這個方法中的this指向需要檢測的值。返回的格式為[object XX]
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全域性物件 global 的引用
需要注意的是
toString()方法在每種類原型中都有,但是除了Object的原型,其他的都是把資料轉換為字串的意思。
淺拷貝
淺拷貝就是隻複製指向某個物件的指標,而不復制物件本身,新舊物件共享同一塊記憶體。
淺拷貝的實現方法
直接賦值
let a = 1;
let b = a;
b = 2;
console.log(a); //2
console.log(b); //2
Object.assign()
let obj = {name:'Carol', age:{child:12}}
let copy = Object.assign({},obj);
copy.name = 'Lee';
copy.age.child = 24;
console.log(obj);// {name:'Lee',age:{child:24}}
函式實現
function shallowClone (source){
if(!source || typeof source != 'object'){
throw new Error ('error');
}
var targetObj = source.constructor === Array ? [] : {};
for(var keys in source) {
if(source.hasOwnProperty(keys)){
targetObj[keys] = source[keys];
}
}
return targetObj;
}
深拷貝
複製並建立一個一模一樣的物件,不共享記憶體,修改新物件,舊物件保持不變。
深拷貝的實現方式
函式實現
//使用迴圈遍歷的方式實現陣列、物件的深拷貝
function deepClone(obj) {
//判斷拷貝的要進行深拷貝的是陣列還是物件,是陣列的話進行陣列拷貝,物件的話進行物件拷貝
var objClone = Array.isArray(obj) ? [] : {};
//進行深拷貝的不能為空,並且是物件
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
借用JSON的parse()和stringify()方法
//借用JSON物件的stringify把物件轉為字串,然後用parse轉成新的物件
function deepClone(obj){
let _obj = JSON.stringify(obj),
let objClone = JSON.parse(_obj);
return objClone;
}
let a=[0,1,[2,3],4],
let b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);