js變數的數據類型值分基本類型值和引用類型值。 在ES6(ECMAScript6)以前,基本數據類型包括String、Number、Boolean、Undefined、Null。 基本類型值的複製(拷貝) 從一個變數向另一個變數複製基本類型的值。使用賦值操作符 ' = ' 即可。 如: 上述代碼定義 ...
js變數的數據類型值分基本類型值和引用類型值。
在ES6(ECMAScript6)以前,基本數據類型包括String、Number、Boolean、Undefined、Null。
基本類型值的複製(拷貝)
從一個變數向另一個變數複製基本類型的值。使用賦值操作符 ' = ' 即可。
如:
1 var num1 = 1, num2; 2 num2 = num1;
上述代碼定義了兩個變數num1、num2。num1初始化值是1,num2是undefined。接著把num1賦值給num2。
num1的值與num2的值增刪改減完全獨立、互不影響。
1 ++num1; 2 num2 = null; 3 // 2 null
拓展:基於基本類型值,ES提供了三個特殊的引用類型。String、Number、Boolean。(基本包裝類型)
1 var num3 = 3; 2 var num4 = num3.toFixed(2); 3 console.log(num3, num4); // 3 3.00
如上,變數num3包含一個數字值,數字當然屬於基本類型值啦,接著num3調用了toFixed()方法。並將返回結果保存在num4中。最後在控制台輸出下。結果是3 3.00。當然了,沒有報錯。。
一般來理解,基本類型值不是對象,不應該有方法。(但是它們確實有方法.。查看它們有哪些方法的一個辦法是在chrome控制台console.log(new Number(1))。其他基本類型值同理。baidu/翻書/強記。do whatever you want)
當第二行代碼訪問num3時,訪問過程處於一種讀取模式,也就是從記憶體中讀取這個變數的值。此時,在後臺,大概是執行了下列的es代碼:
1 var _num3 = new Number(3);// 創建Number類型的一個實例 2 var _num4 = _num3.toFixed(2);// 在實例上調用指定的方法 3 _num3 = null;// 銷毀這個實例 4 return _num4;// 可以想象成在一個函數里執行這裡的4行代碼,函數返回_num4。接著被num4接收。
這也意味著我們可以對基本類型值做一些擴展。比如:
1 var num5 = 1; 2 Number.prototype.addTen = function () { 3 var res = this + 10; 4 return res; 5 }; 6 console.log(num5.addTen());// 11
如上,在Number原型上定義addTen()方法,所有Number類型值都可以調用這個方法。
其他基本類型值同理。
ES6規範引入了一項新特性--symbol,它也是一種基本數據類型,它的功能類似於一種標識唯一性的ID。
調用Symbol函數來創建一個Symbol實例:
1 const S1 = Symbol(); 2 // 可以在調用Symbol函數時傳入一個參數,相當於給你創建的Symbol實例一個描述信息。參數可選,可以是任意可轉化成字元串的值。 3 const S2 = Symbol('id9527');
引用類型的複製(拷貝)
常見的引用類型包括 Object、Aarry、Date、Function、RegExp...
引用類型值是引用類型的一個實例。
通過賦值操作符‘=’複製的引用類型值。實際上複製的是一個指針(地址)。該指針指向存儲在堆中的對象。
1 const obj1 = new Object(); 2 const obj2 = obj1; 3 obj1.name = 'xm'; 4 console.log(obj2.name);// xm
obj1與obj2指向同一個對象,對obj1的修改,同樣作用於obj2。
多數時候這不是我們想要的結果。我們需要的是兩個相互獨立而又長得一模一樣的對象。
由於引用類型值也可能包含引用類型值。由此就派生出了淺拷貝和深拷貝。
淺拷貝
數組的淺拷貝常用方法:
(1)concat方法
1 const arr1 = ['a', 'b', ['c', 4]]; 2 const arr2 = arr1.concat([]); 3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false
(2)slice方法
1 const arr1 = ['a', 'b', ['c', 4]]; 2 const arr2 = arr1.slice(0); 3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false
(3)擴展運算符...方法
1 const arr1 = ['a', 'b', ['c', 4]]; 2 const arr2 = [...arr1]; 3 // const [...arr2] = arr1; // 等同於上一行 4 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false
(4)map方法
1 const arr1 = ['a', 'b', ['c', 4]]; 2 const arr2 = arr1.map(item => item); 3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false
(5)filter方法 把上面的map改成filter即可。
...for迴圈、forEach、for of、splice、Object.values等方法均可。
對象的淺拷貝常用方法:
1、for in遍歷方法
1 const obj = { 2 say(){ 3 console.log('hello'); 4 } 5 }; 6 const obj1 = Object.create(obj); 7 obj1.a = '對象'; 8 obj1.b = [1, 2, 3]; 9 10 // const obj2 = Object.create(obj); // 繼承obj的屬性、方法 11 const obj2 = {}; 12 for (let p in obj1) { 13 if (obj1.hasOwnProperty(p)) { 14 obj2[p] = obj1[p]; 15 } 16 }
如上,obj1的原型對象是obj,淺拷貝一般不需要拷貝原型上的屬性和方法,而for in迴圈可以枚舉原型上的屬性和方法。使用hasOwnProperty()方法過濾掉原型的屬性和方法。
結果如下:
(2)Object.entries()方法
1 const obj = { 2 say(){ 3 console.log('hello'); 4 } 5 }; 6 7 const obj1 = Object.create(obj); 8 obj1.a = '對象'; 9 obj1.b = [1, 2, 3]; 10 11 // const obj2 = Object.create(obj); // 繼承obj的屬性、方法 12 const obj2 = {}; 13 Object.entries(obj1).forEach(([key, value]) => obj2[key] = value);結果如下:
之所以稱為淺拷貝,其原因在於如果引用類型值里包含引用類型值,上述的所有方法,在對裡層的引用類型值複製操作時,使用的還是賦值操作符'='。如下所示:
如果修改了obj1.b的值,同樣會作用於obj2。
深拷貝
以下是實現對數組、對象深拷貝的一種方法。
採用遞歸的方式,層層遍歷。
1 const deepClone = function handleDeepClone(obj) { 2 if (typeof obj !== 'object' || obj === null) { 3 return obj; 4 } 5 6 let _obj; 7 if (obj instanceof Array) { 8 _obj = []; 9 obj.forEach((item, i) => _obj[i] = handleDeepClone(item)); 10 } else { 11 _obj = {}; 12 Object.entries(obj).map(([key, value]) => _obj[key] = handleDeepClone(value)); 13 } 14 15 return _obj; 16 };
結果如下: