首頁  >  文章  >  web前端  >  JavaScript的資料型態與變數的解析(附範例)

JavaScript的資料型態與變數的解析(附範例)

不言
不言轉載
2018-11-15 17:07:012138瀏覽

這篇文章帶給大家的內容是關於JavaScript的資料型別與變數的解析(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

這篇文章,來聊聊 JS 中的資料型別與變數。這是在學習 JS 時最基礎的一類問題,但卻很重要。希望我的分享有幫助到你。

文章開頭,我先提幾個面試中遇到的問題:

例如:

#如何理解參數的按值傳遞?

什麼是暫時性死區?

什麼是變數提升?

全域變數和 window 的屬性有什麼差別?為什麼?

... ...

這篇文章的風格,在分析知識點的同時,插入一些我經歷過的面試題目。

基本資料型態

在 JS 中,基本資料型別有 6 種,分別是數值、字串、布林值、null、undefined、Symbol。

對於基本資料類型,我們需要明白的是:基本類型在記憶體中的儲存方式是堆疊。每一個數值都是單獨存放,互不影響。

基本類型都是按值存取的。在比較時,按值進行比較:

1 === 1 // true

引用資料類型

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

引用類型按引用存取。在比較時,也比較的參考:

{} === {} // => false

參數的傳遞方式

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

這裡要分析的是參數是以哪一種型別傳遞的?引用型別還是基本型別?

先看一個基礎的例子:

var out_num = 1;

function addOne(in_num) {
    in_num += 1;
    return in_num;
}

addOne(out_num); // => 2
out_num // => 1

這個例子中,我們給addOne() 函數傳遞一個實參out_num,這個時out_num 會傳遞給in_num,即內部存在著in_num = out_num 的過程。最後我們看到的結果是 out_num 並沒有被函數改變,說明 in_num 和 out_num 是兩個在記憶體中獨立存放的值,也就是按值傳遞。

再來看一個變形:

var out_obj = { value: 1 };

function addOne(in_obj) {
    in_obj.value += 1;
    return in_obj;
}

addOne(out_obj); // => { value: 2 }
out_obj // => { value: 2 }

問題來了?函數參數不是按值傳遞嗎?為什麼這裡函數內部的處理反映到外部了? 這是一個超級超級超級的理解迷思。

首先,我們還是得擺正觀點,也就是函數參數是按值傳遞的。 那這裡怎麼理解呢?對於引用型別而言,前面說引用型別分為引用和實際的記憶體空間。這裡 out_obj 依舊傳遞給 in_obj,即 in_obj = out_objout_obj 和 in_obj 是兩個引用,它們在記憶體中的儲存方式是獨立的,但是它們卻指向同一塊記憶體。

in_obj.value = 1 則是直接操作的實際物件。實際物件的改變,會同步到所有引用這個實際物件的引用。

JavaScript的資料型態與變數的解析(附範例)

JavaScript的資料型態與變數的解析(附範例)

你再來看這個例子,或許會更清楚一些。

var out_obj = { value: 1 };

function addOne(in_obj) {
    in_obj = { value: 2 };
    return in_obj;
}

addOne(out_obj); // => { value: 2 }
out_obj // => { value: 1 }

你只要抓住一點:物件的賦值就會造成引用指向的實際物件改變。

如何判斷資料型別

判斷資料型別,通常有三種具體的方法:

1、typeof 運算子

typeof 操作符傳回一個表示資料類型的字串。它有以下明顯的缺陷:

typeof null // => 'object'

typeof [] // => 'object'

這是因為在 JS 語言設計之初遺留的 bug。可以閱讀這篇文章 http://2ality.com/2013/10/typ... 了解更多關於 typeof 處理 null 的問題。

所以 typeof 最好用來判斷一些基本類型,像是數值、字串、布林值、undefined、Symbol。

2、instanceof 運算子

typeof  的背後是透過判斷type tags 來判斷資料類型,而instanceof 則是透過判斷建構函數的prototype 是否出現在物件原型鏈上的任何位置。

舉例:

{} instanceof Object // => true

[] instanceof Array // => true
[] instanceof Object // => true

也判斷自訂類型:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
var auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// => true

console.log(auto instanceof Object);
// => true

所以,對於字面量形式的基本資料類型,不能透過instanceof 來判斷:

1 instanceof Number // => false

Number(1) instanceof Number // => false

new Number(1) instanceof Number // => true

3、Object.prototype.toString()

這是目前最推薦的一種方法,可以更精細且準確的判斷任何資料類型,甚至是JSON、正規、日期、錯誤等等。在 Lodash 中,其判斷資料型別的核心也是 Object.prototype.toString() 方法。

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

關於這背後的原理,你可以閱讀這篇文章http://www.cnblogs.com/ziyunf...

4、其他

上面三種是通用的判斷資料類型的方法。面試中也會出現如何判斷一個陣列、如何判斷 NaN、如何判斷類別陣列物件、如何判斷一個空物件等問題。這一類問題比較開放,解決想法通常是抓住判斷資料的核心特徵。

舉個例子:判斷類別數組物件。

你先要知道 JS 中类数组对象是什么样子的,并寻求一个实际的参照物,比如 arguments 就是类数组对象。那么类数组对象具有的特点是:真值 & 对象 & 具有 length 属性 & length 为整数 & length 的范围大于等于 0,小于等于最大安全正整数(Number.MAX_SAFE_INTEGER)。

在你分析特点的时候,答案就呼之欲出了。【注意全面性】

数据类型如何转换

JS 数据类型的动态性将贯穿整个 JS 的学习,这是 JS 非常重要的特性,很多现象就是因为动态性的存在而成为 JS 独有。

正是由于动态性,JS 的数据类型可能在你毫无察觉的情况下,就发生了改变,直到运行时报错。

这里主要分析下面 8 种转换规则。

1、if 语句

if 语句中的类型转换是最常见的。

if (isTrue) {
    // ...
} else {}

在 if 语句中,会自动调用 Boolean() 转型函数对变量 isTrue 进行转换。

当 isTrue 的值是 null, undefined, 0, NaN, '' 时,都会转为 false。其余值除 false 本身外都会转为 true。

2、Number() 转型函数

我们重点关注 null undefined 以及字符串在 Number() 下的转换:

Number(null) // => 0
Number(undefined) // => NaN
Number('') // => 0
Number('123') // => 123
Number('123abc') // => NaN

注意和 parseInt() 对比。

3、parseInt()

parseInt(null) // => NaN
parseInt(undefined) // => NaN
parseInt('') // => NaN
parseInt('123') // => 123
parseInt('123abc') // => 123

4、==

这里需要注意的是:

null == undefined // => true

null == 0 // => false
undefined == false // => false

null 与 undefined 的相等性是由 ECMA-262 规定的,并且 null 与 undefined 在比较相等性时不能转换为其他任何值。

5、关系操作符

对于两个字符串的比较,是比较的字符编码值:

'B'  true

一个数值,另一个其他类型,都将转为数字进行比较。

两个布尔值转为数值进行比较。

对象,先调用 valueOf(),若不存在该方法,则调用 toString()。

6、加法

加法中特别注意的是,数字和字符串相加,将数字转为字符串。

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

对于对象和布尔值,调用它们的 toString() 方法得到对应的字符串值,然后进行字符串相加。对于 undefined 和 null 调用 String() 取得字符串 'undeifned' 和 'null'。

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

7、减法

对于字符串、布尔值、null 或者 undefined,自动调用 Number(),转换结果若为 NaN,那么最终结果为 NaN。

对于对象,先调用 valueOf(),如果得到 NaN,结果为 NaN。如果没有 valueOf(),则调用 toString()。

8、乘法、除法

对于非数值,都会调用 Number() 转型函数。

变量提升与暂时性死区

JS 中有三种声明变量的方式:var, let, const。

var 声明变量最大的一个特点是存在变量提升。

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

第一个打印结果表示,在声明变量 a 之前,a 就已经可以访问了,只不过并未赋值。这就是变量提升现象。(具体原因,我放在后面分析作用域的时候来写)

let 和 const 就不存在这个问题,但是又引入了暂时性死区这样的概念。

/**
* 这上面都属于变量 a 的暂时性死区
* console.log(a) // => Reference Error
*/
let a = 1;
console.log(a); // => 1

即声明 a 之前,不能够访问 a,而直接报错。

而暂时性死区的出现又引出另外一个问题,即 typeof 不再安全。你可以参考这篇文章 http://es-discourse.com/t/why...

补充:一个经典面试题

for (var i = 0; i <p>我先不再这里展开分析,我打算放到异步与事件循环机制中去分析。不过这里将 var 替换成 let 可以作为一种解决方案。如果你有兴趣,也可以先去分析。</p><p>对于 const,这里再补充一点,用于加深对基本类型和引用类型的理解。</p><pre class="brush:php;toolbar:false">const a = 1;
const b = { value: 1 };

a = 2; // => Error
b.value = 2; // => 2
b = { value: 2 }; // => Error

本质上,const 并不是保证变量的值不得改动,而是变量指向的内存地址不得改动。

声明全局变量

直接通过 var 声明全局变量,这个全局变量会作为 window 对象的一个属性。

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

在这里提出两个问题,一是 let 声明的全局变量会成为 window 的属性吗?二是 var 声明的全局变量和直接在 window 创建属性有没有区别?

先来回答第一问题。let 声明的全局变量不会成为 window 的属性。用什么来支撑这样的结论呢?在 ES6 中,对于 let 和 const 声明的变量从一开始就形成封闭作用域。想想之前的暂时性死区。

第二个问题,var 声明的全局变量和直接在 window 创建属性存在着本质的区别。先看下面的代码:

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

window.b = 2;

delete window.a
delete window.b

window.a // => 1
window.b // => undefined

我们可以看到,直接创建在 window 上的属性可以被 delete 删除,而 var 创建的全局属性则不会。这是现象,通过现象看本质,二者本质上的区别在于:

使用 var 宣告的全域變數的 [[configurable]] 資料屬性的值為 false,不能透過 delete 刪除。而直接在物件上建立的屬性預設 [[configurable]] 的值為 true,即可以被 delete 刪除。 (關於[[configurable]] 屬性,在後面的文章中分析物件的時候還會提到)

小結

在這篇「資料類型與變量」文章中,分析了7 個大類。再來回顧一下:

基本型別、參考型別、參數傳遞方式、如何判斷資料型別、資料型別轉換、變數提升與暫時性死區、宣告全域變數。

這些不僅是校招面試中的高頻考點,也是學習 JS 必不可少的知識點。

Tip1:《JavaScript 高階程式設計》這本書被稱為「前端的聖經」是有原因的。對於正在準備校園招募的你,非常有必要! 書讀百遍,其義自見。 你會發現你在面試中遇到的絕大部分 JS 相關的知識點都能在這本書中找到「答案」!

Tip2:在準備複習的過程中,注意知識的模組性與相關性。你得有自己劃分知識模組的能力,例如今天的「資料類型與變數」模組。相關性是指,任何的知識都是由連結的,例如這裡牽涉到作用域、記憶體等模組。

#

以上是JavaScript的資料型態與變數的解析(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除