一、基本類型VS引用類型
注: 這里的內(nèi)存涩笤,為虛擬內(nèi)存
1蹬碧、引用類型:
- 定義:保存在堆內(nèi)存中的對(duì)象炒刁,變量中保存的實(shí)際上只是一個(gè)指針,這個(gè)指針執(zhí)行內(nèi)存中的另一個(gè)位置罗心,由該位置保存對(duì)象
- 包括:對(duì)象渤闷、數(shù)組脖镀、函數(shù)、正則
假設(shè)變量中有一個(gè)函數(shù)补憾,函數(shù)內(nèi)東西特別多(或者有一個(gè)對(duì)象,對(duì)象里的數(shù)據(jù)特別大)腾务,這里可選堆的空白處存放函數(shù)、對(duì)象的數(shù)據(jù)(隨機(jī)選擇未使用的空白堆窿撬,隨意變大變行鸱病),放在堆中的均為引用類型
2跛璧、基本類型(值類型):
- 定義:指的是保存在棧內(nèi)存中的簡(jiǎn)單字段(成塊排列追城,棧燥撞,允許放進(jìn)去拿出來)
- 包括:數(shù)值(number)物舒、布爾值(boolean)、
null
锋玲、undefined
涵叮、string
(在賦值傳遞中會(huì)以引用類型的方式來處理)
棧里面仍存有變量割粮,只不過存放的不是數(shù)據(jù),而是大數(shù)據(jù)地址廷雅,比如這個(gè)地址為0x0011
,棧內(nèi)存放的東西京髓,均為可控堰怨、較小容量。從一個(gè)變量向另一個(gè)變量賦值基本類型時(shí)灿巧,會(huì)在該變量上創(chuàng)建一個(gè)新值抠藕,然后再把該值復(fù)制到為新變量分配的位置上。
3敬辣、實(shí)例一:基本類型
var a
var b
var obj
var obj2
a = 1;
b = 2;
var obj = {
name: 'xiaoqin',
sex: 'male',
age: 30,
friend: {
name: 'hello', age: 100
}
}
var newObj = {}
b = a;
console.log(b)
//返回1
如圖:
(1)基本類型的值被賦值給另一個(gè)變量购岗,其實(shí)就是分配內(nèi)存空間
一開始门粪,a
的值為 1 玄妈,當(dāng)使用a
來初始化b
時(shí),b
值此時(shí)為1绎签。但b
中的1與a
中的是完全獨(dú)立的诡必,該值只是a
中的值的一個(gè)副本搔扁。說明在棧里變量再次變化稿蹲,但這個(gè)兩個(gè)變量可以參加任何操作而相互不受影響。
總結(jié):
一個(gè)變量賦值給另一個(gè)變量時(shí)涂炎,其實(shí)是分配了一塊新的內(nèi)存空間设哗。按照以上操作网梢,基本類型在賦值操作后,事實(shí)上就a
分配了一塊新內(nèi)存空間給b
蛀序,兩個(gè)變量是相互不受影響活烙。
(2)基本類型的比較是值的比較 只有在它們的值相等的時(shí)候它們才相等啸盏。 當(dāng)比較的兩個(gè)值的類型不同的時(shí)候==
運(yùn)算符會(huì)進(jìn)行類型轉(zhuǎn)換,但是當(dāng)兩個(gè)值的類型相同的時(shí)候气笙,即使是==
也相當(dāng)于是===
潜圃。
var a = 1;
var b = true;
console.log(a == b);//true
(3)基本類型的變量其實(shí)就是存放在棧區(qū)舟茶。結(jié)合以上吧凉,棧區(qū)指內(nèi)存里的棧內(nèi)存,但是棧區(qū)里包括了變量名和變量值胀瞪。
4饲鄙、實(shí)例二:(續(xù)上面的例子)引用類型
(1)引用類型的值是可變的
可為引用類型添加屬性和方法忍级,也可以刪除其屬性和方法颤练。
看一下這個(gè)例子:一個(gè)為引用類型的變量賦值給另一個(gè)為引用類型的變量
var obj2 = obj //控制臺(tái)測(cè)試一下二者的值
obj
// {name: "ruoyu", sex: "male", age: 30, friend: {…}}
obj2
// {name: "ruoyu", sex: "male", age: 30, friend: {…}}
值是一樣的。因為var obj2=obj
患雇,即通過obj
的值(一個(gè)對(duì)象)賦值給obj2
宇挫,那么obj2
的值就是賦值后原本obj
對(duì)應(yīng)屬性和值器瘪。作為一個(gè)引用類型,它被放在堆中援所。所以尋找obj2
則在堆里找到住拭,只是換了另一個(gè)名字為obj2
如圖:
總結(jié):
原本在棧中的對(duì)象分別指向了同一個(gè)堆滔岳,那么存放在堆中即為對(duì)象的內(nèi)存地址。引用類型的賦值其實(shí)是對(duì)象保存在棧區(qū)地址指針的賦值摊求,因此兩個(gè)變量指向同一個(gè)對(duì)象室叉,任何的操作都會(huì)相互影響寥闪。
(2)引用類型的比較是引用的比較
A疲憋、我們先看一下基本類型值的比較:
var obj3 = '{name: 'hello'}';
var obj4 = '{name: 'hello'}';
console.log( obj3 == obj4 );
// true
總結(jié):
可以得出基本類型的比較:當(dāng)兩個(gè)比較值的類型相同(如字符串)的時(shí)候缚柳,相當(dāng)于是用 ===
,所以輸出是true
彩掐。
B堵幽、再來看一下引用類型值的比較:
var obj3 = {name: 'hello'}
var obj4 = {name: 'hello'}
obj3 === obj4
//返回false弹澎,說明二者并不相等
為什么是false?不相等呢殴胧?
放在棧中的變量 obj3
团滥、obj4
,聲明前置均為undefined
拱燃,當(dāng)兩者均被被聲明值的時(shí)候扼雏,是兩個(gè)對(duì)象,引用類型是引用訪問苍蔬,相當(dāng)于在堆中分別開辟了兩個(gè)空間碟绑,堆中會(huì)有對(duì)應(yīng)的屬性+值俺猿,此時(shí)這兩個(gè)對(duì)象在堆中存的便是堆的地址。obj4
與obj3
一樣都開辟了新的堆空間格仲,但是存放的地址也不一樣押袍。判斷obj3
是否與obj4
相等,看了分析之后凯肋,便知道堆存放的地址并不同谊惭,二者也就不相等
如圖:
(3)引用類型的值是同時(shí)保存在棧內(nèi)存和堆內(nèi)存中的對(duì)象
function sum(){
console.log('sum...')
}
var sum2 = sum;
sum2()
//返回sum... 二者是相等的
我們可以就此分析,函數(shù)function sum()
,分別有變量sum
和函數(shù)對(duì)象代碼(為引用類型侮东,已放在堆中)圈盔。之后sum賦值給sum2
悄雅,即sum2
事實(shí)上使用的是賦值后sum
所指代堆的內(nèi)存地址,即后續(xù)sum
和sum2
共用了堆里的代碼(變量的內(nèi)存地址就像指針一樣驱敲,通過JS自身引擎找到這個(gè)堆),一堆東西起了兩個(gè)不同的名字
如圖:
總結(jié): js不同于其他語(yǔ)言宽闲,其不允許直接訪問內(nèi)存中的位置众眨,即不能直接操作對(duì)象的內(nèi)存空間,實(shí)際上容诬,是操作對(duì)象的引用娩梨,所以引用類型的值是按引用訪問的。
準(zhǔn)確地說放案,引用類型的存儲(chǔ)需內(nèi)存的棧區(qū)(棧區(qū)是指內(nèi)存里的棧內(nèi)存)和堆區(qū)(堆區(qū)是指內(nèi)存里的堆內(nèi)存)共同完成姚建,棧區(qū)內(nèi)存保存變量標(biāo)識(shí)符和指向堆內(nèi)存中該對(duì)象的指針,然后吱殉,棧區(qū)內(nèi)存地址也可以說是該對(duì)象在堆內(nèi)存的地址掸冤。
二厘托、引用類型的實(shí)際應(yīng)用
1、函數(shù)的參數(shù)傳遞
第1個(gè)例子:
function inc(n){
n++;
}
var a = 10;
inc(a)
console.log(a)
//返回10
//等同于
function inc(){
var n = arguments[0]
n++
}
//在函數(shù)的一開始將var a = 10賦值進(jìn)var n = arguments[0]稿湿, //n=arguments[0]=10,此時(shí)與n++為11并沒有返回铅匹,所以與a并無關(guān)系
var a = 10
inc(a)
console.log(a)
//返回10
?第2個(gè)例子:
function incObj(obj){
//var obj = o //0x0001
obj.n++
}
var o = {n: 10} //o = 0x0001 對(duì)其做聲明,為一個(gè)對(duì)象
incObj(o)
console.log(o)
//等同于
function incObj(){
var obj =arguments[0]
obj.n++
}
//incObj(o) 相當(dāng)于function incObj(){var obj =arguments[0]饺藤;obj.n++}包斑,
//可知道obj=arguments[0]=o,相當(dāng)于設(shè)obj為臨時(shí)變量,而o= 0x0001 var o = {n: 10} incObj(o) console.log(o)
如圖:
總結(jié):
引用類型的本質(zhì)涕俗,變量所存的是這個(gè)對(duì)象的內(nèi)存地址指向堆罗丰,當(dāng)去做賦值時(shí)是把這個(gè)地址進(jìn)行一個(gè)賦值;當(dāng)去訪問的時(shí)候是通過這個(gè)地址去訪問這個(gè)對(duì)象
?第3個(gè)例子:
function squireArr( arr ){
//var arr = 0x0011
for(var i = 0; i < arr.length; i++){
arr[i] = arr[i] * arr[i];
}
}
var arr = [2,1,3,6]
squireArr(arr)
console.log(arr)
//(4) [4, 1, 9, 36]
即把function squireArr(arr){}
中的數(shù)組squireArr(arr)
里的每一項(xiàng)變?yōu)樵瓉淼钠椒皆俟茫磪?shù)arr
為數(shù)組里的值萌抵,用for循環(huán)進(jìn)行操作,外界調(diào)用時(shí)元镀,只需調(diào)用一次squireArr(arr)
绍填,事實(shí)上數(shù)組squireArr(arr)
操作就是對(duì)arr
的操作
?第4個(gè)例子:
function squireArr2( arr ){
var newArr = [];
for(var i = 0; i < arr.length; i++){
newArr[i] = arr[i] * arr[i];
}
return newArr;
}
var arr2 = squireArr2(arr)
console.log(arr2) //返回(4) [16, 1, 81, 1296]
arr
// (4) [4, 1, 9, 36]
arr2 --> (4) [16, 1, 81, 1296]
2、對(duì)象的深淺拷貝
針對(duì)這個(gè)例子:
var obj;
var obj2;
var obj = {
name: 'ruoyu',
sex: 'male',
age: 30,
friend: {
name: 'hello',
age: 100
}
}
var obj2 = obj;
想要?jiǎng)?chuàng)造一個(gè)新的b栖疑,那么就需要遍歷原始a的每一項(xiàng)屬性+
值讨永,用來獲取成為新個(gè)體的b所需的東西,并一一對(duì)b進(jìn)行改造遇革,即從一無所有卿闹,改造成與a相似的新個(gè)體,此為克隆澳淑。
如果在遍歷的時(shí)候比原,b這個(gè)新個(gè)體只是遍歷a的前半部分或者局部,那么這稱之為淺拷貝杠巡,如:
function shallowCopy(oldObj) {
var newObj = {};
for(var i in oldObj) {
if(oldObj.hasOwnProperty(i)) {
newObj[i] = oldObj[i];
}
}
return newObj;
}
而如果b是遍歷原始a的每一項(xiàng)屬性和值量窘,但是b又是一個(gè)獨(dú)立個(gè)體,與a不相關(guān)氢拥,當(dāng)修改b的時(shí)候蚌铜,a仍然不會(huì)發(fā)生變化,而這叫做深拷貝嫩海,如:
function deepCopy(oldObj) {
var newObj = {};
for(var key in oldObj) {
if(typeof oldObj[key] === 'object') {
newObj[key] = deepCopy(oldObj[key]);
}else{
newObj[key] = oldObj[key];
}
}
return newObj;
}
json——string——對(duì)象