自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

分析JavaScript的數(shù)據(jù)類型與變量

開(kāi)發(fā) 前端
這篇文章,我們來(lái)聊聊 JS 中的數(shù)據(jù)類型與變量。這是最基礎(chǔ)的一類問(wèn)題,但卻很重要。

這篇文章,我們來(lái)聊聊 JS 中的數(shù)據(jù)類型與變量。這是最基礎(chǔ)的一類問(wèn)題,但卻很重要。

比如:

如何理解參數(shù)的按值傳遞?

什么是暫時(shí)性死區(qū)?

什么是變量提升?

全局變量和 window 的屬性有什么區(qū)別?為什么?

... ...

以上的問(wèn)題均來(lái)自面試。如果你并不清楚,我覺(jué)得你有必要接著讀下去。

基本數(shù)據(jù)類型

在 JS 中,基本數(shù)據(jù)類型有 6 種,即數(shù)值、字符串、布爾值、null、undefined、Symbol。

對(duì)于基本數(shù)據(jù)類型,我們需要明白的是:基本類型在內(nèi)存中的存儲(chǔ)方式是棧。每一個(gè)值都是單獨(dú)存放,互不影響。

基本類型都是按值訪問(wèn)的。在比較時(shí),按值進(jìn)行比較: 

  1. 1 === 1 // true 

引用數(shù)據(jù)類型

引用類型的值保存在堆中,而引用是保存在棧中。

引用類型按引用訪問(wèn)。在比較時(shí),也比較的引用: 

  1. {} === {} // => false 

參數(shù)的傳遞方式

在 JS 中,參數(shù)可以是任何類型的值,甚至可以是函數(shù)。

這里要分析的是參數(shù)是以哪種類型傳遞的?引用類型還是基本類型?

先看一個(gè)基礎(chǔ)的例子: 

  1. var out_num = 1 
  2. function addOne(in_num) {  
  3. in_num += 1;  
  4. return in_num;  
  5.  
  6. addOne(out_num); // => 2  
  7. out_num // => 1  

這個(gè)例子中,我們給 addOne() 函數(shù)傳遞一個(gè)實(shí)參 out_num,這個(gè)時(shí) out_num 會(huì)傳遞給 in_num,即內(nèi)部存在著 in_num = out_num 的過(guò)程。***我們看到的結(jié)果是 out_num 并沒(méi)有被函數(shù)改變,說(shuō)明 in_num 和 out_num 是兩個(gè)在內(nèi)存中獨(dú)立存放的值,即按值傳遞。

再來(lái)看一個(gè)變形: 

  1. var out_obj = { value: 1 };  
  2. function addOne(in_obj) {  
  3. in_obj.value += 1;  
  4. return in_obj;  
  5.  
  6. addOne(out_obj); // => { value: 2 }  
  7. out_obj // => { value: 2 }  

問(wèn)題來(lái)了?函數(shù)參數(shù)不是按值傳遞嗎?為什么這里函數(shù)內(nèi)部的處理反映到外部了?這是一個(gè)超級(jí)超級(jí)超級(jí)的理解誤區(qū)。

首先,我們還是得擺正觀點(diǎn),即函數(shù)參數(shù)是按值傳遞的。那這里怎么理解呢?對(duì)于引用類型而言,前面說(shuō)引用類型分為引用和實(shí)際的內(nèi)存空間。在這里 out_obj 依舊傳遞給 in_obj,即 in_obj = out_obj ,out_obj 和 in_obj 是兩個(gè)引用,它們?cè)趦?nèi)存中的存儲(chǔ)方式是獨(dú)立的,但是它們卻指向同一塊內(nèi)存。

而 in_obj.value = 1 則是直接操作的實(shí)際對(duì)象。實(shí)際對(duì)象的改變,會(huì)同步到所有引用這個(gè)實(shí)際對(duì)象的引用。 

 

你再來(lái)看這個(gè)例子,或許就會(huì)更清晰一些。 

  1. var out_obj = { value: 1 };  
  2. function addOne(in_obj) {  
  3. in_obj = { value: 2 };  
  4. return in_obj;  
  5.  
  6. addOne(out_obj); // => { value: 2 }  
  7. out_obj // => { value: 1 } 

你只要抓住一點(diǎn):對(duì)象的賦值就會(huì)造成引用指向的實(shí)際對(duì)象發(fā)生改變。

如何判斷數(shù)據(jù)類型

判斷數(shù)據(jù)類型,通常有三種具體的方法:

1、typeof 操作符

typeof 操作符返回一個(gè)表示數(shù)據(jù)類型的字符串。它存在以下明顯的缺陷: 

  1. typeof null // => 'object'  
  2. typeof [] // => 'object'  

這是因?yàn)樵?JS 語(yǔ)言設(shè)計(jì)之初遺留的 bug??梢蚤喿x這篇文章 http://2ality.com/2013/10/typ... 了解更多關(guān)于 typeof 處理 null 的問(wèn)題。

所以 typeof ***用于判斷一些基本類型,比如數(shù)值、字符串、布爾值、undefined、Symbol。

2、instanceof 操作符

typeof 的背后是通過(guò)判斷 type tags 來(lái)判斷數(shù)據(jù)類型,而 instanceof 則是通過(guò)判斷構(gòu)造函數(shù)的 prototype 是否出現(xiàn)在對(duì)象原型鏈上的任何位置。

舉個(gè)例子: 

  1. {} instanceof Object // => true  
  2. [] instanceof Array // => true  
  3. [] instanceof Object // => true  

也判斷自定義類型: 

  1. function Car(make, model, year) {  
  2. this.make = make;  
  3. this.model = model;  
  4. this.year = year;  
  5.  
  6. var auto = new Car('Honda', 'Accord', 1998);  
  7. console.log(auto instanceof Car);  
  8. // => true  
  9. console.log(auto instanceof Object);  
  10. // => true  

所以,對(duì)于字面量形式的基本數(shù)據(jù)類型,不能通過(guò) instanceof 判斷: 

  1. 1 instanceof Number // => false  
  2. Number(1) instanceof Number // => false  
  3. new Number(1) instanceof Number // => true  

3、Object.prototype.toString()

這是目前最為推薦的一種方法,可以更加精細(xì)且準(zhǔn)確的判斷任何數(shù)據(jù)類型,甚至是 JSON、正則、日期、錯(cuò)誤等等。在 Lodash 中,其判斷數(shù)據(jù)類型的核心也是 Object.prototype.toString() 方法。 

  1. Object.prototype.toString.call(JSON) // => "[object JSON]" 

關(guān)于這背后的原理,你可以閱讀這篇文章 http://www.cnblogs.com/ziyunf...

4、其他

上面三種是通用的判斷數(shù)據(jù)類型的方法。面試中還會(huì)出現(xiàn)如何判斷一個(gè)數(shù)組、如何判斷 NaN、如何判斷類數(shù)組對(duì)象、如何判斷一個(gè)空對(duì)象等問(wèn)題。這一類問(wèn)題比較開(kāi)放,解決思路通常是抓住判斷數(shù)據(jù)的核心特點(diǎn)。

舉個(gè)例子:判斷類數(shù)組對(duì)象。

你先要知道 JS 中類數(shù)組對(duì)象是什么樣子的,并尋求一個(gè)實(shí)際的參照物,比如 arguments 就是類數(shù)組對(duì)象。那么類數(shù)組對(duì)象具有的特點(diǎn)是:真值 & 對(duì)象 & 具有 length 屬性 & length 為整數(shù) & length 的范圍大于等于 0,小于等于***安全正整數(shù)(Number.MAX_SAFE_INTEGER)。

在你分析特點(diǎn)的時(shí)候,答案就呼之欲出了?!咀⒁馊嫘浴?/p>

數(shù)據(jù)類型如何轉(zhuǎn)換

JS 數(shù)據(jù)類型的動(dòng)態(tài)性將貫穿整個(gè) JS 的學(xué)習(xí),這是 JS 非常重要的特性,很多現(xiàn)象就是因?yàn)閯?dòng)態(tài)性的存在而成為 JS 獨(dú)有。

正是由于動(dòng)態(tài)性,JS 的數(shù)據(jù)類型可能在你毫無(wú)察覺(jué)的情況下,就發(fā)生了改變,直到運(yùn)行時(shí)報(bào)錯(cuò)。

這里主要分析下面 8 種轉(zhuǎn)換規(guī)則。

1、if 語(yǔ)句

if 語(yǔ)句中的類型轉(zhuǎn)換是最常見(jiàn)的。 

  1. if (isTrue) {  
  2. // ...  
  3. } else {}  

在 if 語(yǔ)句中,會(huì)自動(dòng)調(diào)用 Boolean() 轉(zhuǎn)型函數(shù)對(duì)變量 isTrue 進(jìn)行轉(zhuǎn)換。

當(dāng) isTrue 的值是 null, undefined, 0, NaN, '' 時(shí),都會(huì)轉(zhuǎn)為 false。其余值除 false 本身外都會(huì)轉(zhuǎn)為 true。

2、Number() 轉(zhuǎn)型函數(shù)

我們重點(diǎn)關(guān)注 null undefined 以及字符串在 Number() 下的轉(zhuǎn)換: 

  1. Number(null) // => 0  
  2. Number(undefined) // => NaN  
  3. Number('') // => 0  
  4. Number('123') // => 123  
  5. Number('123abc') // => NaN  

注意和 parseInt() 對(duì)比。

3、parseInt() 

  1. parseInt(null) // => NaN  
  2. parseInt(undefined) // => NaN  
  3. parseInt('') // => NaN  
  4. parseInt('123') // => 123  
  5. parseInt('123abc') // => 123  

4、==

這里需要注意的是: 

  1. null == undefined // => true  
  2. null == 0 // => false  
  3. undefined == false // => false  

null 與 undefined 的相等性是由 ECMA-262 規(guī)定的,并且 null 與 undefined 在比較相等性時(shí)不能轉(zhuǎn)換為其他任何值。

5、關(guān)系操作符

對(duì)于兩個(gè)字符串的比較,是比較的字符編碼值: 

  1. 'B' < 'a' // => true 

一個(gè)數(shù)值,另一個(gè)其他類型,都將轉(zhuǎn)為數(shù)字進(jìn)行比較。

兩個(gè)布爾值轉(zhuǎn)為數(shù)值進(jìn)行比較。

對(duì)象,先調(diào)用 valueOf(),若不存在該方法,則調(diào)用 toString()。

6、加法

加法中特別注意的是,數(shù)字和字符串相加,將數(shù)字轉(zhuǎn)為字符串。 

  1. '1' + 2 => // '12'  
  2. 1 + 2 => // 3  

對(duì)于對(duì)象和布爾值,調(diào)用它們的 toString() 方法得到對(duì)應(yīng)的字符串值,然后進(jìn)行字符串相加。對(duì)于 undefined 和 null 調(diào)用 String() 取得字符串 'undeifned' 和 'null'。 

  1. { value: 1 } + true // => "[object Object]true" 

7、減法

對(duì)于字符串、布爾值、null 或者 undefined,自動(dòng)調(diào)用 Number(),轉(zhuǎn)換結(jié)果若為 NaN,那么最終結(jié)果為 NaN。

對(duì)于對(duì)象,先調(diào)用 valueOf(),如果得到 NaN,結(jié)果為 NaN。如果沒(méi)有 valueOf(),則調(diào)用 toString()。

8、乘法、除法

對(duì)于非數(shù)值,都會(huì)調(diào)用 Number() 轉(zhuǎn)型函數(shù)。

變量提升與暫時(shí)性死區(qū)

JS 中有三種聲明變量的方式:var, let, const。

var 聲明變量***的一個(gè)特點(diǎn)是存在變量提升。 

  1. console.log(a); // undefined  
  2. var a = 1 
  3. console.log(a); // 1  

***個(gè)打印結(jié)果表示,在聲明變量 a 之前,a 就已經(jīng)可以訪問(wèn)了,只不過(guò)并未賦值。這就是變量提升現(xiàn)象。(具體原因,我放在后面分析作用域的時(shí)候來(lái)寫(xiě))

let 和 const 就不存在這個(gè)問(wèn)題,但是又引入了暫時(shí)性死區(qū)這樣的概念。 

  1. /**  
  2. * 這上面都屬于變量 a 的暫時(shí)性死區(qū)  
  3. * console.log(a) // => Reference Error  
  4. */  
  5. let a = 1 
  6. console.log(a); // => 1  

即聲明 a 之前,不能夠訪問(wèn) a,而直接報(bào)錯(cuò)。

而暫時(shí)性死區(qū)的出現(xiàn)又引出另外一個(gè)問(wèn)題,即 typeof 不再安全。你可以參考這篇文章 http://es-discourse.com/t/why...

補(bǔ)充:一個(gè)經(jīng)典面試題 

  1. for (var i = 0; i < 4; i++) {  
  2. setTimeout(function(){  
  3. console.log(i);  
  4. }, i * 1000);  
  5.  

我先不再這里展開(kāi)分析,我打算放到異步與事件循環(huán)機(jī)制中去分析。不過(guò)這里將 var 替換成 let 可以作為一種解決方案。如果你有興趣,也可以先去分析。

對(duì)于 const,這里再補(bǔ)充一點(diǎn),用于加深對(duì)基本類型和引用類型的理解。 

  1. const a = 1 
  2. const b = { value: 1 };  
  3. a = 2; // => Error  
  4. b.value = 2; // => 2  
  5. b = { value: 2 }; // => Error  

本質(zhì)上,const 并不是保證變量的值不得改動(dòng),而是變量指向的內(nèi)存地址不得改動(dòng)。

聲明全局變量

直接通過(guò) var 聲明全局變量,這個(gè)全局變量會(huì)作為 window 對(duì)象的一個(gè)屬性。 

  1. var a = 1 
  2. window.a // => 1  

在這里提出兩個(gè)問(wèn)題,一是 let 聲明的全局變量會(huì)成為 window 的屬性嗎?二是 var 聲明的全局變量和直接在 window 創(chuàng)建屬性有沒(méi)有區(qū)別?

先來(lái)回答***問(wèn)題。let 聲明的全局變量不會(huì)成為 window 的屬性。用什么來(lái)支撐這樣的結(jié)論呢?在 ES6 中,對(duì)于 let 和 const 聲明的變量從一開(kāi)始就形成封閉作用域。想想之前的暫時(shí)性死區(qū)。

第二個(gè)問(wèn)題,var 聲明的全局變量和直接在 window 創(chuàng)建屬性存在著本質(zhì)的區(qū)別。先看下面的代碼: 

  1. var a = 1 
  2. window.a // => 1  
  3. window.b = 2 
  4. delete window.a  
  5. delete window.b  
  6. window.a // => 1  
  7. window.b // => undefined  

我們可以看到,直接創(chuàng)建在 window 上的屬性可以被 delete 刪除,而 var 創(chuàng)建的全局屬性則不會(huì)。這是現(xiàn)象,通過(guò)現(xiàn)象看本質(zhì),二者本質(zhì)上的區(qū)別在于:

使用 var 聲明的全局變量的 [[configurable]] 數(shù)據(jù)屬性的值為 false,不能通過(guò) delete 刪除。而直接在對(duì)象上創(chuàng)建的屬性默認(rèn) [[configurable]] 的值為 true,即可以被 delete 刪除。(關(guān)于 [[configurable]] 屬性,在后面的文章中分析對(duì)象的時(shí)候還會(huì)提到)

小結(jié)

在這篇「數(shù)據(jù)類型與變量」文章中,分析了 7 個(gè)大類。再來(lái)回顧一下:

基本類型、引用類型、參數(shù)傳遞方式、如何判斷數(shù)據(jù)類型、數(shù)據(jù)類型如何轉(zhuǎn)換、變量提升與暫時(shí)性死區(qū)、聲明全局變量。

這些不僅是校招面試中的高頻考點(diǎn),也是學(xué)習(xí) JS 必不可少的知識(shí)點(diǎn)。

 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2023-10-17 07:57:56

Python數(shù)據(jù)類型

2016-08-18 14:13:55

JavaScript基本數(shù)據(jù)引用數(shù)據(jù)

2011-07-29 10:12:12

JavaScript

2010-10-08 15:11:28

JavaScript數(shù)

2021-12-03 15:24:45

Javascript數(shù)據(jù)類型

2022-03-01 23:31:29

Python編程語(yǔ)言變量

2010-08-10 17:17:59

2024-01-12 09:00:22

Swift常量數(shù)據(jù)類型

2010-10-08 09:02:03

JavaScript基

2017-10-24 14:05:16

MySQLSchema數(shù)據(jù)類型

2017-02-27 08:34:09

JavaScript數(shù)據(jù)引用

2023-03-27 10:04:27

數(shù)據(jù)類型浮點(diǎn)型布爾型

2020-12-29 07:56:23

JavaScript數(shù)據(jù)類型 primitive

2010-08-11 09:14:33

DB2數(shù)據(jù)類型

2019-08-12 11:40:48

數(shù)據(jù)庫(kù)SQLite3數(shù)據(jù)類型

2011-08-24 16:24:54

Lua源代碼

2020-10-26 13:46:07

Java基礎(chǔ)入門篇

2022-08-12 16:12:34

JavaScript數(shù)據(jù)類型字符串

2023-04-06 07:49:23

Python數(shù)據(jù)類型

2010-07-22 17:47:32

SQL Server數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)