JavaScript中原始值和引用值深入講解
值和引用相關(guān)內(nèi)容
在 JavaScript 中,數(shù)據(jù)類(lèi)型整體上來(lái)講可以分為兩大類(lèi):基本類(lèi)型和引用數(shù)據(jù)類(lèi)型
基本數(shù)據(jù)類(lèi)型,一共有 6 種:
string,symbol,number,boolean,undefined,null
其中 symbol 類(lèi)型是在 ES6 里面新添加的基本數(shù)據(jù)類(lèi)型。
引用數(shù)據(jù)類(lèi)型,就只有 1 種:
object
基本數(shù)據(jù)類(lèi)型的值又被稱(chēng)之為原始值或簡(jiǎn)單值,而引用數(shù)據(jù)類(lèi)型的值又被稱(chēng)之為復(fù)雜值或引用值。
那么兩者之間具體有什么區(qū)別呢?我們一點(diǎn)一點(diǎn)來(lái)看:
1. 簡(jiǎn)單值(原始值)
簡(jiǎn)單值是表示 JavaScript 中可用的數(shù)據(jù)或信息的最底層形式或最簡(jiǎn)單形式。簡(jiǎn)單類(lèi)型的值被稱(chēng)為簡(jiǎn)單值,是因?yàn)樗鼈兪?/strong>不可細(xì)化的。
也就是說(shuō),數(shù)字是數(shù)字,字符串是字符串,布爾值是 true 或 false,null 和 undefined 就是 null 和 undefined。這些值本身很簡(jiǎn)單,不能夠再進(jìn)行拆分。
由于簡(jiǎn)單值的數(shù)據(jù)大小是固定的,所以簡(jiǎn)單值的數(shù)據(jù)是存儲(chǔ)于內(nèi)存中的棧區(qū)里面的。
要簡(jiǎn)單理解棧的存取方式,我們可以通過(guò)類(lèi)比乒乓球盒子來(lái)分析。如下圖:
下面是具體的代碼示例:
var str = "Hello World"; var num = 10; var bol = true; var myNull = null; var undef = undefined; console.log(typeof str); // string console.log(typeof num); // number console.log(typeof bol); // boolean console.log(typeof myNull); // object console.log(typeof undef); // undefined
這里面 null 比較特殊,打印出來(lái)是 object,這是由于歷史原因所遺留下來(lái)的問(wèn)題。
是來(lái)源于 JavaScript 從第一個(gè)版本開(kāi)始時(shí)的一個(gè) bug,并且這個(gè) bug 無(wú)法被修復(fù)。因?yàn)樾迯?fù)會(huì)破壞現(xiàn)有的代碼。
具體原因是因?yàn)椴煌膶?duì)象在底層都表現(xiàn)為二進(jìn)制,在 JavaScript 中二進(jìn)制前三位都為 0 的話會(huì)被判斷為 object 類(lèi)型,null 的二進(jìn)制全部為 0,自然前三位也是 0,所以執(zhí)行 typeof 值會(huì)返回 object。
例外,當(dāng)我們打印 null == undefined 的時(shí)候,返回的是 true,這也是面試時(shí)經(jīng)常會(huì)被問(wèn)到的一個(gè)問(wèn)題。
這兩個(gè)值都表示“無(wú)”的意思。
通常情況下, 當(dāng)我們?cè)噲D訪問(wèn)某個(gè)不存在的或者沒(méi)有賦值的變量時(shí),就會(huì)得到一個(gè) undefined 值。Javascript 會(huì)自動(dòng)將聲明是沒(méi)有進(jìn)行初始化的變量設(shè)為 undifined。
而 null 值表示空,null 不能通過(guò) Javascript 來(lái)自動(dòng)賦值,也就是說(shuō)必須要我們自己手動(dòng)來(lái)給某個(gè)變量賦值為 null。
那么為什么 JavaScript 要設(shè)置兩個(gè)表示"無(wú)"的值呢?
這其實(shí)也是因?yàn)闅v史原因。
1995 年 JavaScript 誕生時(shí),最初像 Java 一樣,只設(shè)置了 null 作為表示"無(wú)"的值。根據(jù) C 語(yǔ)言的傳統(tǒng),null 被設(shè)計(jì)成可以自動(dòng)轉(zhuǎn)為 0。
但是,JavaScript 的設(shè)計(jì)者,覺(jué)得這樣做還不夠,主要有以下兩個(gè)原因。
- null 像在 Java 里一樣,被當(dāng)成一個(gè)對(duì)象。但是,JavaScript 的數(shù)據(jù)類(lèi)型分成原始類(lèi)型(primitive)和復(fù)合類(lèi)型(complex)兩大類(lèi),作者覺(jué)得表示“無(wú)”的值最好不是對(duì)象。
- JavaScript 的最初版本沒(méi)有包括錯(cuò)誤處理機(jī)制,發(fā)生數(shù)據(jù)類(lèi)型不匹配時(shí),往往是自動(dòng)轉(zhuǎn)換類(lèi)型或者默默地失敗。作者覺(jué)得,如果 null 自動(dòng)轉(zhuǎn)為 0,很不容易發(fā)現(xiàn)錯(cuò)誤。
因此,作者又設(shè)計(jì)了一個(gè) undefined。這里注意:先有 null 后有 undefined 出來(lái),undefined 是為了填補(bǔ)之前的坑。
JavaScript 的最初版本是這樣區(qū)分的:
null 是一個(gè)表示“無(wú)”的對(duì)象(空對(duì)象指針),轉(zhuǎn)為數(shù)值時(shí)為 0;
典型用法是:
- 作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對(duì)象。
- 作為對(duì)象原型鏈的終點(diǎn)。
undefined 是一個(gè)表示"無(wú)"的原始值,轉(zhuǎn)為數(shù)值時(shí)為 NaN。
典型用法是:
- 變量被聲明了,但沒(méi)有賦值時(shí),就等于 undefined。
- 調(diào)用函數(shù)時(shí),應(yīng)該提供的參數(shù)沒(méi)有提供,該參數(shù)等于 undefined。
- 對(duì)象沒(méi)有賦值的屬性,該屬性的值為 undefined。
- 函數(shù)沒(méi)有返回值時(shí),默認(rèn)返回 undefined。
2. 復(fù)雜值(引用值)
在 JavaScript 中,對(duì)象就是一個(gè)復(fù)雜值。因?yàn)閷?duì)象可以向下拆分,拆分成多個(gè)簡(jiǎn)單值或者復(fù)雜值。
復(fù)雜值在內(nèi)存中的大小是未知的,因?yàn)閺?fù)雜值可以包含任何值,而不是一個(gè)特定的已知值,所以復(fù)雜值的數(shù)據(jù)都是存儲(chǔ)于堆區(qū)里面。
如下圖所示:
下面是具體的代碼示例:
// 簡(jiǎn)單值 var a1 = 0; var a2 = "this is str"; var a3 = null // 復(fù)雜值 var c = [1, 2, 3]; var d = {m: 20};
3. 訪問(wèn)方式
按值訪問(wèn)
簡(jiǎn)單值是作為不可細(xì)化的值進(jìn)行存儲(chǔ)和使用的,引用它們會(huì)轉(zhuǎn)移其值。
var str = "Hello"; var str2 = str; str = null; console.log(str,str2); // null "Hello"
引用訪問(wèn)
復(fù)雜值是通過(guò)引用進(jìn)行存儲(chǔ)和操作的,而不是實(shí)際的值。創(chuàng)建一個(gè)包含復(fù)雜對(duì)象的變量時(shí),其值是內(nèi)存中的一個(gè)引用地址。引用一個(gè)復(fù)雜對(duì)象時(shí),使用它的名稱(chēng)(即變量或?qū)ο髮傩裕┩ㄟ^(guò)內(nèi)存中的引用地址獲取該對(duì)象值。
var obj = {}; var obj2 = obj; obj.name = "zhangsan"; console.log(obj.name); // zhangsan console.log(obj2.name); // zhangsan
4. 比較方式
簡(jiǎn)單值采用值比較,而復(fù)雜值采用引用比較。復(fù)雜值只有在引用相同的對(duì)象(即有相同的地址)時(shí)才相等。即使是包含相同對(duì)象的兩個(gè)變量也彼此不相等,因?yàn)樗鼈儾⒉恢赶蛲粋€(gè)對(duì)象。
示例 1:
var a = 10; var b = 10; var c = new Number(10); var d = c; console.log(a === b); // true console.log(a === c); // false console.log(a === c); // false console.log(a == c); // true d = 10; console.log(d == c); // true console.log(d === c); // false
示例 2:
var obj = {name : 'zhangsan'}; var obj2 = {name : 'zhangsan'}; console.log(obj == obj2); // false console.log(obj === obj2); // false var obj3 = {name : 'zhangsan'}; var obj4 = obj3; console.log(obj3 == obj4); // true console.log(obj3 === obj4); // ture
5. 動(dòng)態(tài)屬性
對(duì)于復(fù)雜值,可以為其添加屬性和方法,也可以改變和刪除其屬性和方法。但簡(jiǎn)單值不可以:
var str = 'test'; str.abc = true; console.log(str.abc); // undefined var obj = {}; obj.abc = true; console.log(obj.abc); // true
復(fù)雜值支持動(dòng)態(tài)對(duì)象屬性,因?yàn)槲覀兛梢远x對(duì)象,然后創(chuàng)建引用,再更新對(duì)象,并且所有指向該對(duì)象的變量都會(huì)獲得更新。
一個(gè)新變量指向現(xiàn)有的復(fù)雜對(duì)象,并沒(méi)有復(fù)制該對(duì)象。這就是復(fù)雜值有時(shí)被稱(chēng)為引用值的原因。復(fù)雜值可以根據(jù)需求有任意多個(gè)引用,即使對(duì)象改變,它們也總是指向同一個(gè)對(duì)象
var obj = {name : 'zhangsan'}; var obj2 = obj; var obj3 = obj2; obj.name = 'abc'; console.log(obj.name, obj2.name, obj3.name); // abc abc abc
6. 變量賦值
最后說(shuō)一下關(guān)于變量的賦值,其實(shí)是可以分為直接賦值和引用賦值的。直接賦值,就是指將簡(jiǎn)單值賦值給變量,而引用賦值是指將一個(gè)復(fù)雜值的引用賦值給變量,這個(gè)引用指向堆區(qū)實(shí)際存在的數(shù)據(jù)。
直接賦值
var a = 3; var b = a; b = 5; console.log(a); // 3
引用賦值
var a = {value : 1}; var b = a; b.value = 10; console.log(a.value); // 10
靈魂拷問(wèn)
- JS 的基本數(shù)據(jù)類(lèi)型有哪些?基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的區(qū)別
參考答案:
在 JavaScript 中,數(shù)據(jù)類(lèi)型整體上來(lái)講可以分為兩大類(lèi):基本類(lèi)型和引用數(shù)據(jù)類(lèi)型
基本數(shù)據(jù)類(lèi)型,一共有 6 種:
string,symbol,number,boolean,undefined,null其中 symbol 類(lèi)型是在 ES6 里面新添加的基本數(shù)據(jù)類(lèi)型。
引用數(shù)據(jù)類(lèi)型,就只有 1 種:
object基本數(shù)據(jù)類(lèi)型的值又被稱(chēng)之為原始值或簡(jiǎn)單值,而引用數(shù)據(jù)類(lèi)型的值又被稱(chēng)之為復(fù)雜值或引用值。
兩者的區(qū)別在于:
原始值是表示 JavaScript 中可用的數(shù)據(jù)或信息的最底層形式或最簡(jiǎn)單形式。簡(jiǎn)單類(lèi)型的值被稱(chēng)為原始值,是因?yàn)樗鼈兪?/strong>不可細(xì)化的。
也就是說(shuō),數(shù)字是數(shù)字,字符是字符,布爾值是 true 或 false,null 和 undefined 就是 null 和 undefined。這些值本身很簡(jiǎn)單,不能夠再進(jìn)行拆分。由于原始值的數(shù)據(jù)大小是固定的,所以原始值的數(shù)據(jù)是存儲(chǔ)于內(nèi)存中的棧區(qū)里面的。
在 JavaScript 中,對(duì)象就是一個(gè)引用值。因?yàn)閷?duì)象可以向下拆分,拆分成多個(gè)簡(jiǎn)單值或者復(fù)雜值。引用值在內(nèi)存中的大小是未知的,因?yàn)橐弥悼梢园魏沃担皇且粋€(gè)特定的已知值,所以引用值的數(shù)據(jù)都是存儲(chǔ)于堆區(qū)里面。
最后總結(jié)一下兩者的區(qū)別:
訪問(wèn)方式
- 原始值:訪問(wèn)到的是值
- 引用值:訪問(wèn)到的是引用地址
比較方式
- 原始值:比較的是值
- 引用值:比較的是地址
動(dòng)態(tài)屬性
- 原始值:無(wú)法添加動(dòng)態(tài)屬性
- 引用值:可以添加動(dòng)態(tài)屬性
變量賦值
- 原始值:賦值的是值
- 引用值:賦值的是地址
總結(jié)
到此這篇關(guān)于JavaScript中原始值和引用值的文章就介紹到這了,更多相關(guān)js原始值和引用值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript中Date format(js日期格式化)方法小結(jié)
這篇文章主要介紹了javascript中Date format,即js日期格式化的方法.實(shí)例總結(jié)了三種常見(jiàn)的JavaScript日期格式化技巧,需要的朋友可以參考下2015-12-12Angularjs手動(dòng)解析表達(dá)式($parse)
這篇文章主要介紹了Angularjs手動(dòng)解析表達(dá)式($parse)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10用js實(shí)現(xiàn)隨機(jī)返回?cái)?shù)組的一個(gè)元素
js實(shí)現(xiàn)隨機(jī)返回?cái)?shù)組的一個(gè)元素,這是個(gè)奇妙的方法。適合做標(biāo)題性質(zhì)文字的隨機(jī)輪換顯示2007-08-08使用javaScript實(shí)現(xiàn)鼠標(biāo)拖拽事件
這篇文章主要為大家詳細(xì)介紹了使用javaScript實(shí)現(xiàn)鼠標(biāo)拖拽事件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09JavaScript(js)處理的HTML事件、鍵盤(pán)事件、鼠標(biāo)事件簡(jiǎn)單示例
這篇文章主要介紹了JavaScript(js)處理的HTML事件、鍵盤(pán)事件、鼠標(biāo)事件,結(jié)合實(shí)例形式分析了JavaScript針對(duì)HTML事件、鍵盤(pán)事件及鼠標(biāo)事件的簡(jiǎn)單處理方法,需要的朋友可以參考下2019-11-11js+css實(shí)現(xiàn)三級(jí)導(dǎo)航菜單
這篇文章主要為大家詳細(xì)介紹了js+css實(shí)現(xiàn)三級(jí)導(dǎo)航菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08