JS基本數(shù)據(jù)類型和引用數(shù)據(jù)類型
再講 js 的基本數(shù)據(jù)類型和引用數(shù)據(jù)類型之前,我們先說一下棧和堆的概念
1呛占、棧(stack)和堆(heap)
- 棧(stack):
棧會自動分配內(nèi)存空間晌姚,會自動釋放屿储,存放基本類型鹊奖,簡單的數(shù)據(jù)段,占據(jù)固定大小的空間养篓。 - 堆(heap):
動態(tài)分配的內(nèi)存秃流,大小不定也不會自動釋放,存放引用類型柳弄,指那些可能由多個值構(gòu)成的對象舶胀,保存在堆內(nèi)存中,包含引用類型的變量碧注,實際上保存的不是變量本身嚣伐,而是指向該對象的指針。
2萍丐、基本數(shù)據(jù)類型和引用數(shù)據(jù)類型
基本數(shù)據(jù)類型:Number
轩端、String
、Boolean
碉纺、Null
船万、 Undefined
、Symbol(ES6)
骨田,這些類型可以直接操作保存在變量中的實際值
基本數(shù)據(jù)類型特點:
占用空間固定,保存在棧中
(當(dāng)一個方法執(zhí)行時声怔,每個方法都會建立自己的內(nèi)存棧态贤,在這個方法內(nèi)定義的變量將會逐個放入這塊棧內(nèi)存里,隨著方法的執(zhí)行結(jié)束醋火,
這個方法的內(nèi)存棧也將自然銷毀了悠汽。因此箱吕,所有在方法中定義的變量都是放在棧內(nèi)存中的;棧中存儲的是基礎(chǔ)變量以及一些對象的引用變量柿冲,
基礎(chǔ)變量的值是存儲在棧中茬高,而引用變量存儲在棧中的是指向堆中的數(shù)組或者對象的地址,這就是為何修改引用類型總會影響到其他指向這個地址
的引用變量)保存與復(fù)制的是值本身
使用typeof檢測數(shù)據(jù)的類型
基本類型數(shù)據(jù)是值類型
引用數(shù)據(jù)類型:Object
(在JS中除了基本數(shù)據(jù)類型以外的都是對象假抄,數(shù)據(jù)是對象怎栽,函數(shù)是對象,正則表達式是對象)
引用數(shù)據(jù)類型特點:
占用空間不固定宿饱,保存在堆中
(當(dāng)我們在程序中創(chuàng)建一個對象時熏瞄,這個對象將被保存到運行時數(shù)據(jù)區(qū)中,以便反復(fù)利用(因為對象的創(chuàng)建成本通常較大)
谬以,這個運行時數(shù)據(jù)區(qū)就是堆內(nèi)存强饮。堆內(nèi)存中的對象不會隨方法的結(jié)束而銷毀,即使方法結(jié)束后为黎,這個對象還可能被另一個引用變量所引用(方法的參數(shù)傳遞時很常見)邮丰,
則這個對象依然不會被銷毀,只有當(dāng)一個對象沒有任何引用變量引用它時铭乾,系統(tǒng)的垃圾回收機制才會在核實的時候回收它剪廉。)保存與復(fù)制的是指向?qū)ο蟮囊粋€指針
使用instanceof檢測數(shù)據(jù)類型
使用new()方法構(gòu)造出的對象是引用型
3、基本數(shù)據(jù)類型(Number
片橡、String
妈经、Boolean
、Null
捧书、 Undefined
吹泡、Symbol(ES6)
)存放在棧
中
- 基本數(shù)據(jù)類型是指存放在棧中的簡單數(shù)據(jù)段,數(shù)據(jù)大小確定经瓷,內(nèi)存空間大小可以分配爆哑,它們是直接按值存放的,所以可以直接按值訪問
var a = 1;
var b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
棧內(nèi)存
4舆吮、引用數(shù)據(jù)類型(Object
)存放在堆
中
- 引用類型是存放在堆內(nèi)存中的對象揭朝,變量其實是保存的在棧內(nèi)存中的一個指針(保存的是堆內(nèi)存中的引用地址),這個指針指向堆內(nèi)存
- 引用類型數(shù)據(jù)在棧內(nèi)存中保存的實際上是對象在堆內(nèi)存中的引用地址色冀。通過這個引用地址可以快速查找到保存中堆內(nèi)存中的對象
var a = {
name: 'lily',
age: '16'
}
var b = a;
b.name = 'lokka';
console.log(a); // {name: "lokka", age: "16"}
把 a 賦值給 b 潭袱,就相當(dāng)于把 a 的內(nèi)存地址指向 b ,即 a 和 b 指向同一內(nèi)存地址锋恬, 改變了 b 就相當(dāng)于改變了 a
淺拷貝和深拷貝
1屯换、概念
淺拷貝:只拷貝一層,深層次的對象級別只拷貝引用。
深拷貝:拷貝多層彤悔,每一級別的數(shù)據(jù)都會被拷貝出來嘉抓。
2、淺拷貝的實現(xiàn)方式
- 方法一:通用循環(huán)
function shallowCopy(obj) {
if (typeof obj !== 'object') return;
const newObj = obj instanceof Array ? [] : {};
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
- 方法二:Object.assign
const newObj = Object.assign({}, oldObj);
- 方法三:Array.slice
const newArray = oldArray.slice();
- 方法四:Array.concat
const newArray = oldArray.concat();
- 方法五:es6
const { ...newObj } = oldObj;
const [ ...newArray ] = oldArray;
3晕窑、深拷貝的實現(xiàn)方式
- 方法一:通用循環(huán)
function deepCopy(obj) {
if (typeof obj !== 'object') return;
const newObj = obj instanceof Array ? [] : {};
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
- 方法二:JSON.parse抑片、JSON.stringify
const newObj = JSON.parse(JSON.stringify(oldObj));