一、在JavaScript的數據中包含如下兩種
1 基本類型javascript
<pre>Number、Boolean、String、NULL、Undefined</pre>以及ES6的<pre>Symbol</pre>java
2 引用類型es6
<pre>Object、Array、Function、Date</pre>算法
二、在內存中位置的不一樣
- 基本類型:佔用空間固定,保存在棧中;
- 引用類型:佔用空間不固定,保存在堆中;
棧(stack)爲自動分配的內存空間,它由系統自動釋放;使用一級緩存,被調用時一般處於存儲空間中,調用後被當即釋放。 堆(heap)則是動態分配的內存,大小不定也不會自動釋放。使用二級緩存,生命週期與虛擬機的GC算法有關。segmentfault
當一個方法執行時,每一個方法都會創建本身的內存棧,在這個方法內定義的變量將會逐個放入這塊棧內存裏,隨着方法的執行結束,這個方法的內存棧也將天然銷燬了。所以,全部在方法中定義的變量都是放在棧內存中的;棧中存儲的是基礎變量以及一些對象的引用變量,基礎變量的值是存儲在棧中,而引用變量存儲在棧中的是指向堆中的數組或者對象的地址,這就是爲什麼修改引用類型總會影響到其餘指向這個地址的引用變量。數組
當咱們在程序中建立一個對象時,這個對象將被保存到運行時數據區中,以便反覆利用(由於對象的建立成本一般較大),這個運行時數據區就是堆內存。堆內存中的對象不會隨方法的結束而銷燬,即便方法結束後,這個對象還可能被另外一個引用變量所引用(方法的參數傳遞時很常見),則這個對象依然不會被銷燬,只有當一個對象沒有任何引用變量引用它時,系統的垃圾回收機制纔會在覈實的時候回收它。緩存
三、賦值、淺複製、深複製
- 對於基本類型值,賦值、淺拷貝、深拷貝時都是複製基本類型的值給新的變量,以後二個變量之間操做不在相互影響。
- 對於引用類型值, 賦值後二個變量指向同一個地址,一個變量改變時,另外一個也一樣改變; 淺拷貝後獲得一個新的變量,這個與以前的已經不是指向同一個變量,改變時不會使原數據中的基本類型一同改變,但會改變會原數據中的引用類型數據 深拷貝後獲得的是一個新的變量,她的改變不會影響元數據
類型 | 和原數據是否指向同一對象 | 第一層數據爲基本數據類型 | 原數據中包含子對象 |
---|---|---|---|
賦值 | 是 | 改變會使原數據一同改變 | 改變會使原數據一同改變 |
淺拷貝 | 否 | 改變不會使原數據一同改變 | 改變會使原數據一同改變 |
深拷貝 | 否 | 改變不會使原數據一同改變 | 改變不會使原數據一同改變 |
var obj1 = { 'name' : 'zhangsan', 'age' : '18', 'language' : [1,[2,3],[4,5]], }; var obj2 = obj1; var obj3 = shallowCopy(obj1); function shallowCopy(src) { var dst = {}; for (var prop in src) { if (src.hasOwnProperty(prop)) { dst[prop] = src[prop]; } } return dst; } obj2.name = "lisi"; obj3.age = "20"; obj2.language[1] = ["二","三"]; obj3.language[2] = ["四","五"]; console.log(obj1); //obj1 = { // 'name' : 'lisi', // 'age' : '18', // 'language' : [1,["二","三"],["四","五"]], //}; console.log(obj2); //obj2 = { // 'name' : 'lisi', // 'age' : '18', // 'language' : [1,["二","三"],["四","五"]], //}; console.log(obj3); //obj3 = { // 'name' : 'zhangsan', // 'age' : '20', // 'language' : [1,["二","三"],["四","五"]], //};
2.一、淺拷貝
數組經常使用的淺拷貝方法有<pre>slice,concat,Array.from() </pre>,以及es6的析構函數
var arr1 = [1, 2,{a:1,b:2,c:3,d:4}]; var arr2 = arr1.slice(); var arr3 = arr1.concat(); var arr4 = Array.from(arr1); var arr5 = [...arr1]; arr2[0]=2; arr2[2].a=2; arr3[0]=3; arr3[2].b=3; arr4[0]=4; arr4[2].c=4; arr5[0]=5; arr5[2].d=5; // arr1[1,2,{a:2,b:3,c:4,d:5}] // arr2[2,2,{a:2,b:3,c:4,d:5}] // arr3[3,2,{a:2,b:3,c:4,d:5}] // arr4[4,2,{a:2,b:3,c:4,d:5}] // arr5[5,2,{a:2,b:3,c:4,d:5}]
對象經常使用的淺拷貝方法Object.assign(),es6析構url
var obj1 = { x: 1, y: { m: 1 } }; var obj2 = Object.assign({}, obj1); console.log(obj1) //{x: 1, y: {m: 1}} console.log(obj2) //{x: 1, y: {m: 1}} obj2.x=2; obj2.y.m = 2; //修改obj2.y.m console.log(obj1) //{x: 1, y: {m: 2}} console.log(obj2) //{x: 2, y: {m: 2}}
咱們本身實現一個淺拷貝spa
var obj = { a:1, arr: [2,3] }; var shallowObj = shallowCopy(obj); var shallowCopy = function(obj) { // 只拷貝對象 if (typeof obj !== 'object') return; // 根據obj的類型判斷是新建一個數組仍是對象 var newObj = obj instanceof Array ? [] : {}; // 遍歷obj,而且判斷是obj的屬性才拷貝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
2.2深拷貝
比較簡單粗暴的的作法是使用JSON.parse(JSON.stringify(obj))
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}] var new_arr = JSON.parse( JSON.stringify(arr) ); new_arr[4].old=4; console.log(arr); //['old', 1, true, ['old1', 'old2'], {old: 1}] console.log(new_arr); //['old', 1, true, ['old1', 'old2'], {old: 4}]
JSON.parse(JSON.stringify(obj)) 看起來很不錯,不過MDN文檔 的描述有句話寫的很清楚:
undefined、任意的函數以及 symbol 值,在序列化過程當中會被忽略(出如今非數組對象的屬性值中時)或者被轉換成 null(出如今數組中時)。
可是在平時的開發中JSON.parse(JSON.stringify(obj))已經知足90%的使用場景了。 下面咱們本身來實現一個
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
三、參數傳遞
全部的函數參數都是按值傳遞。也就是說把函數外面的值賦值給函數內部的參數,就和把一個值從一個變量賦值給另外一個同樣;
- 基本類型
var a = 2; function add(x) { return x = x + 2; } var result = add(a); console.log(a, result); // 2 4
- 引用類型
function setName(obj) { obj.name = 'laowang'; obj = new Object(); obj.name = 'Tom'; } var person = new Object(); setName(person); console.log(person.name); //laowang
不少人錯誤地覺得在局部做用域中修改的對象在全局做用域中反映出來就是說明參數是按引用傳遞的。 可是經過上面的例子能夠看出若是person是按引用傳遞的最終的person.name應該是Tom。 實際上當函數內部重寫obj時,這個變量引用的就是一個局部變量了。而這個變量會在函數執行結束後銷燬。(這是是在js高級程序設計看到的,還不是很清楚)
四、判斷方法
基本類型用typeof,引用類型用instanceof
特別注意typeof null是"object", null instanceof Object是true;
console.log(typeof "Nicholas"); // "string" console.log(typeof 10); // "number" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" var items = []; var obj = {}; function reflect(value){ return value; } console.log(items instanceof Array); // true; console.log(obj instanceof Object); // true; console.log(reflect instanceof Function); // true;