JavaScript 值

本篇文章主要介紹 JavaScript 中幾個常用的內(nèi)置值類型旁舰。

1. 數(shù)組

JavaScript 中,數(shù)組可以容納任意類型的值嗡官,可以是 string箭窜、numberobject衍腥,也可以是其他數(shù)組(多維數(shù)組)磺樱,聲明數(shù)組后加入值也不需要預先設(shè)定數(shù)組的長度大小纳猫。

1.1 稀疏數(shù)組

稀疏數(shù)組(sparse array)是指索引不連續(xù),數(shù)組長度大于元素個數(shù)的數(shù)組竹捉,通俗說就是含有空白或空缺單元的數(shù)組芜辕。

1.1.1 稀疏數(shù)組生成方式

// 構(gòu)造函數(shù)聲明一個沒有元素的數(shù)組
var a = new Array(5); // [empty × 5]

// 指定的索引值大于數(shù)組長度
var a = [];
a[5] = 4; // [empty × 5, 4]

// 指定大于元素個數(shù)的數(shù)組長度
var a = [];
a.length = 5; // [empty × 5]

// 數(shù)組直接量中省略值
var a = [0, , , ,]; // [0, empty × 3]

// 刪除數(shù)組元素
var a = [0, 1, 2, 3, 4];
delete a[4]; // [0, 1, 2, 3, empty]

1.1.2 emptyundefined

稀疏數(shù)組在控制臺中的表示如下:

var a = new Array(5);
console.log(a); // [empty × 5]

empty × 5 即表示數(shù)組有 5 個空缺單元。但是 empty 并非 JavaScript 的基本數(shù)據(jù)類型块差,當嘗試訪問數(shù)組中的元素時侵续,JavaScript 會返回一個 undefined,這是因為 JavaScript 引擎在發(fā)現(xiàn)元素缺失時會臨時賦值 undefined憨闰。

console.log(a[0]); // undefined

但是 emptyundefined 表示的并不是一個含義状蜗,empty 表示的是當前數(shù)組元素沒有值,空缺的鹉动;而 undefined 則表示當前元素是存在值的轧坎,并且值是 undefined。如下:

var a = new Array(5);
var b = [undefined, undefined, undefined];

a.forEach(i => { console.log(i) });    // 無 log 輸出训裆,無元素不會執(zhí)行回調(diào)
b.forEach(i => { console.log(i) });    // undefined undefined undefined

1.1.3 稀疏數(shù)組的遍歷

  • forEach眶根、filtersome边琉、every 方法

這些方法在遍歷到稀疏數(shù)組的缺失元素時,回調(diào)函數(shù)不會執(zhí)行记劝。

var a = [1,,,,];
a.forEach(i => { console.log(i) }); // 只會打印一次 1
  • for...in

for-in 語句只會遍歷對象的可枚舉屬性变姨,不會遍歷稀疏數(shù)組中的缺失元素。

var a = [1,,,,5];
for (var i in a) { console.log(a[i]) };    // 1 5
  • for...of厌丑、for 循環(huán)定欧、findfinedIndex 方法

for...of 和 for 循環(huán)都會將空缺元素的值替換為 undefined怒竿。find砍鸠、finedIndex 是通過 for 循環(huán)實現(xiàn)的,所以同 for 循環(huán)耕驰。

var a = [1,,,,5];
for (var i of a) { console.log(i) };       // 1 undefined undefined undefined 5
  • includes 方法

includes 方法則比較特殊爷辱,可以理解為當數(shù)組為空時,只會返回 false;而當數(shù)組非空(指長度不為0的數(shù)組,其中包括全部元素都缺失的數(shù)組)跃巡,且函數(shù)調(diào)用參數(shù)為空時會返回 true享幽。

var a = [1,,,,];
var b = new Array(5);
var c = [];

a.includes();                         // true
b.includes();                         // true
c.includes();                         // false
  • map 方法

不會遍歷缺失元素,但返回的結(jié)果具有與源數(shù)組相同的長度和空隙水援。

var a = [1,,,,5];
a.map(i => i);       // [1, empty × 3, 5]
  • sort 方法

不會遍歷缺失元素,數(shù)組能正常排序,同時會返回與源數(shù)組相同的長度阀趴。

var a = [5,,,,1];
a.sort();    // [1, 5, empty × 3]
  • join 方法

缺失元素占的坑還是會被保留昏翰。

var a = new Array(5);
a.join();    // ",,,,"

1.1.4 稀疏數(shù)組轉(zhuǎn)密集數(shù)組

可以通過如下兩個方法實現(xiàn),轉(zhuǎn)換規(guī)則是將空缺元素使用 undefined 代替:

// 稀疏數(shù)組
var a = new Array(5);

Array.apply(null, a);    // ES5 [undefined, undefined, undefined, undefined, undefined]
Array.from(a);           // ES6 [undefined, undefined, undefined, undefined, undefined]

1.1.5 稀疏數(shù)組特性

稀疏數(shù)組跟密集數(shù)組相比具有以下特性:

  • 訪問速度慢
  • 內(nèi)存利用率高

這與 V8 引擎構(gòu)建 JS 對象的方式有關(guān)刘急。V8 訪問對象有兩種模式:字典模式快速模式矩父。

稀疏數(shù)組使用的是字典模式,也稱為散列表模式排霉,該模式下 V8 使用散列表來存儲對象屬性窍株。由于每次訪問時都需要計算哈希值(實際上只需要計算一次,哈希值會被緩存)和尋址攻柠,所以訪問速度非常慢球订。另一方面,對比起使用一段連續(xù)的內(nèi)存空間來存儲稀疏數(shù)組瑰钮,散列表的方式會大幅度地節(jié)省內(nèi)存空間冒滩。

而密集數(shù)組在內(nèi)存空間中是被存儲在一個連續(xù)的類數(shù)組里,引擎可以直接通過數(shù)組索引訪問到數(shù)組元素浪谴,所以速度會非晨快。

如下是一個 jsperf 測試:

// Sparse Array
var a = [];
a[10000] = 1;
a.forEach(function(){});

// Dense Array
var b = Array.from(a);
b.forEach(function(){});
稀疏數(shù)組.png

可見密集數(shù)組的訪問性能明顯比稀疏數(shù)組的高苟耻,因此建議日常編碼中能避免稀疏數(shù)組的盡量避免篇恒。

1.2 類數(shù)組

類數(shù)組的兩個條件

  • 具有:指向?qū)ο笤氐臄?shù)字索引下標以及 length 屬性告訴我們對象的元素個數(shù)

  • 不具有:諸如 push、forEach 以及 indexOf 等數(shù)組對象具有的方法

1.2.1 類數(shù)組:NodeList

如下凶杖,通過 querySelectorAll 獲取到的 NodeList胁艰,有 length,可以通過下表訪問到具體的元素智蝠,不能調(diào)用數(shù)組的方法腾么。所以它就是一個類數(shù)組。

const arrayLike = document.querySelectorAll("div");

console.log(Object.prototype.toString.call(arrayLike)); // [object NodeList]

console.log(arrayLike.length); // 127

console.log(arrayLike[0]);
// <div id="js-pjax-loader-bar" class="pjax-loader-bar"></div>

console.log(Array.isArray(arrayLike)); // false

arrayLike.push("push");
// Uncaught TypeError: arrayLike.push is not a function(…)

1.2.2 類數(shù)組對象

如下就是通過一個對象創(chuàng)建出來的類數(shù)組杈湾。

const arrayLikeObj = {
  length: 2,
  0: "This is Array Like Object",
  1: true,
};

1.2.3 類數(shù)組函數(shù)

const arrayLikeFunc1 = function () {};
console.log(arrayLikeFunc1.length); // 0
const arrFunc1 = Array.prototype.slice.call(arrayLikeFunc1, 0);
console.log(arrFunc1, arrFunc1.length); // [], 0

1.2.4 類數(shù)組轉(zhuǎn)化為數(shù)組

// 數(shù)組slice方法
Array.prototype.slice.call(arrLike);

// Array.from
Array.from(arrLike);

2. 字符串

字符串和數(shù)組的確很相似解虱,它們都是類數(shù)組,都有 length 屬性以及 indexOf(..)concat(..) 方法

var a = "foo";
var b = ["f", "o", "o"];

a.length; // 3
b.length; // 3

a.indexOf("o"); // 1
b.indexOf("o"); // 1

a.concat("bar"); // foobar
b.concat(["b", "a", "r"]); // ["f","o","o","b","a","r"]

JavaScript 中字符串是不可變的漆撞,而數(shù)組是可變的殴泰。

var a = "foo";
var b = ["f", "o", "o"];

a[1] = "O";
b[1] = "O";

a; // "foo",不可改變
b; // ["f","O","o"]叫挟,可變

字符串不可變是指字符串的成員函數(shù)不會改變其原始值艰匙,而是創(chuàng)建并返回一個新的字符串。而數(shù)組的成員函數(shù)都是在其原始值上進行操作抹恳。

var a = "foo";

c = a.toUpperCase();
a === c; // false
a; // "foo"
c; // "FOO"

有些數(shù)組非變更的函數(shù)(不會改變原數(shù)組)员凝,可以用來處理字符串,如下:

var a = "foo";

a.join; // undefined
a.map; // undefined

var c = Array.prototype.join.call(a, "-");
var d = Array.prototype.map
  .call(a, function (v) {
    return v.toUpperCase() + ".";
  })
  .join("");

c; // "f-o-o"
d; // "F.O.O."

3. 數(shù)字

JavaScript 只有一種數(shù)值類型:number奋献,它可以表示整數(shù)和帶小數(shù)的十進制數(shù)健霹。

JavaScript 的數(shù)字類型是基于 IEEE 754 標準實現(xiàn)旺上,并且使用的是雙精度格式,即 64 位二進制糖埋。

雙精度浮點格式(64 位):1 位符號位宣吱、11 位有效數(shù)字位和** 52 位指數(shù)位**。

由于 JavaScript 的數(shù)字值可以使用 Number 對象進行封裝瞳别,因此可以直接調(diào)用 Number.prototype 中的方法征候。

(42).toFixed(3); // "42.000"
(0.42).toFixed(3); // "0.420"
(42).toFixed(3); // "42.000"

如下是無效語法,因為 . 被看作為 42. 的一部分祟敛,所以沒有 . 屬性訪問符來調(diào)用 toFixed 方法疤坝。. 運算符會被優(yōu)先識別為數(shù)字常量的一部分,然后才是對象屬性訪問運算符馆铁。

42.toFixed( 3 ); // SyntaxError

但是下面的語法是有效的(注意其中的空格):

(42).toFixed(3); // "42.000"

十六進制0xf3 or 0Xf3

八進制0o363 or 0O363

二進制0b11110011 or 0B11110011

以上進制的前綴盡量使用小寫字母跑揉。

4.1 較小的數(shù)字

從數(shù)學角度下面的計算應(yīng)該為 true,但是結(jié)果為 false埠巨。這是因為數(shù)值的運算都會轉(zhuǎn)換為二進制历谍,而小數(shù)部分的二進制有些數(shù)字無法精準表示出來,在有限的位數(shù)下辣垒,就會進行誤差取舍望侈,所以導致最終結(jié)果的不精確。

0.1 + 0.2 === 0.3; // false

解決此問題最常見的做法就是設(shè)置一個誤差范圍值乍构,通常是 2^-52甜无,這個值在 JavaScript 中被定義在Number.EPSILON 中,或者使用 Math.pow(2,-52)

function numbersCloseEnoughToEqual(n1, n2) {
  return Math.abs(n1 - n2) < Number.EPSILON;
}

numbersCloseEnoughToEqual(0.1 + 0.2, 0.3); // true
numbersCloseEnoughToEqual(0.0000001, 0.0000002); // false

4.2 整數(shù)的安全范圍

能夠被“安全”呈現(xiàn)的最大整數(shù)是 2^53 - 1哥遮,即 9007199254740991,在 ES6 中被定義為 Number.MAX_SAFE_INTEGER陵究。
最小整數(shù)是 -9007199254740991眠饮, 在 ES6 中 被 定 義 為 Number.MIN_SAFE_INTEGER

超過此范圍的值铜邮,應(yīng)該轉(zhuǎn)換為字符串展示仪召,或者借助相關(guān)的工具庫。

對于數(shù)位操作松蒜,最大支持 32 位的數(shù)字扔茅,超過 32 位的將會被忽略

5. 原生函數(shù)

JavaScript 的內(nèi)建函數(shù)(built-in function),也叫原生函數(shù)(native function)秸苗,常用的原生函數(shù)如下:

String召娜、NumberBoolean惊楼、Array玖瘸、Object秸讹、FuctionRegExp雅倒、Date璃诀、ErrorSymbol蔑匣。

5.1 內(nèi)部屬性 [[Class]]

所有 typeof 返回值為 "object" 的對象(如數(shù)組)都包含一個內(nèi)部屬性 [[Class]]劣欢,可以看作為一個內(nèi)部的分類。這個屬性一般無法直接訪問裁良,一般通過 Object.prototype.toString 來查看凿将,如下:

Object.prototype.toString.call([1, 2, 3]); // "[object Array]"

Object.prototype.toString.call(/regex-literal/i); // "[object RegExp]"

5.2 封裝對象

由于基本類型沒有屬性和方法,需要通過封裝對象才能訪問趴久,此時 JavaScript 會自動為基本類型值包裝一個封裝對象:

var a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"

如果需要經(jīng)常用到這些字符串屬性和方法丸相,比如在 for 循環(huán)中使用 i < a.length,那么從 一開始就創(chuàng)建一個封裝對象也許更為方便彼棍,這樣 JavaScript 引擎就不用每次都自動創(chuàng)建了灭忠。

但實際證明這并不是一個好辦法,因為瀏覽器已經(jīng)為 .length 這樣的常見情況做了性能優(yōu)化座硕,直接使用封裝對象來“提前優(yōu)化”代碼反而會降低執(zhí)行效率弛作。

一般情況下,我們不需要直接使用封裝對象华匾。最好的辦法是讓 JavaScript 引擎自己決定什 么時候應(yīng)該使用封裝對象映琳。換句話說,就是應(yīng)該優(yōu)先考慮使用 "abc" 和 42 這樣的基本類型 值蜘拉,而非 new String("abc")new Number(42)萨西。

5.3 拆封對象

如果想要得到封裝對象中的基本類型值,可以使用 valueOf() 函數(shù):

var a = new String("abc");
var b = new Number(42);
var c = new Boolean(true);

a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true

在需要用到封裝對象中的基本類型值的地方會發(fā)生隱式拆封旭旭,如下:

var a = new String("abc");
var b = a + ""; // b的值為"abc"

typeof a; // "object"
typeof b; // "string"

5.4 構(gòu)造函數(shù)

關(guān)于數(shù)組(array)谎脯、對象(object)、函數(shù)(function)和正則表達式持寄,我們通常喜歡以常 量的形式來創(chuàng)建它們源梭。實際上,使用常量和使用構(gòu)造函數(shù)的效果是一樣的(創(chuàng)建的值都是通過封裝對象來包裝)稍味。

5.4.1 Array(...)

構(gòu)造函數(shù) Array() 不要求帶 new 關(guān)鍵字废麻,不帶的時候會自動補上。因此 Array(1, 2, 3)new Array(1, 2, 3)
效果是一樣的模庐。

需要注意的是烛愧,Array 只有一個數(shù)字參數(shù)的時候,創(chuàng)建出來的是該數(shù)字長度的空數(shù)組,多個數(shù)字的時候屑彻,創(chuàng)建的則是擁有這些數(shù)字的數(shù)組验庙。

5.4.2 Object(...)、Function(...)社牲、RegExp(...)

除非萬不得已粪薛,否則盡量不要使用 Object(..)/Function(..)/RegExp(..)

5.4.3 Date(...) 和 Error(...)

創(chuàng)建日期對象必須使用 new Date()。Date(...)可以帶參數(shù)搏恤,用來指定日期和時間违寿,而不帶 參數(shù)的話則使用當前的日期和時間。

構(gòu)造函數(shù) Error(...)(與前面的 Array() 類似)帶不帶 new 關(guān)鍵字都可熟空。

5.4.4 Symbol(...)

Symbol 比較特殊藤巢,不能帶 new 關(guān)鍵字,否則會出錯息罗。

6. 對象

6.1 語言 BUG null

通過 typeof null 的結(jié)果是 object掂咒,但是 null 本身是一個基本類型,這是 JavaScript 語言中的一個 BUG迈喉。

這是因為不同的對象在底層都表示為二進制绍刮,在 JavaScript 中二進制前三位都為 0 的話會被判 斷為 object 類型,
null 的二進制表示全部都是 0挨摸,自然前三位也是 0孩革,所以執(zhí)行 typeof 時會返回“object”。

6.2 對象屬性的存在性

6.2.1 屬性訪問

通過屬性訪問返回值是否是 undefined得运,可以判斷屬性是否存在膝蜈,但是這個屬性也有可能存儲的就是 undefined,此時就沒法區(qū)分了熔掺。

6.2.2 in 操作符

in 操作符會檢查屬性是否在對象及其 [[Prototype]] 原型鏈中饱搏。

var myObject = { a: 2 };

"a" in myObject; // true
"b" in myObject; // false

這里需要注意的是,如果對數(shù)組進行使用 in 操作符時置逻,檢查的不是數(shù)組的值窍帝,而是數(shù)組的下標。

6.2.3 hasOwnProperty 方法

hasOwnProperty(..) 只會檢查屬性是否在對象中诽偷,不會檢查 [[Prototype]] 鏈。

var myObject = { a: 2 };

myObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("b"); // false

6.2.4 propertyIsEnumerable 方法

propertyIsEnumerable(..) 會檢查給定的屬性名是否直接存在于對象中(而不是在原型鏈上)并且滿足 enumerable: true疯坤。

6.2.5 Object.keys(..)Object.getOwnPropertyNames(..)

這兩個方法都只會查找對象直接包含的屬性报慕。

6.2.6 可枚舉性

從下面代碼可以看出來,myObject.b 確實存在并且有訪問值压怠,但是卻不會出現(xiàn)在 for..in 循環(huán)中眠冈。原因是“可枚舉”就相當于“可以出現(xiàn)在對象屬性的遍歷中”。

var myObject = {};
Object.defineProperty(
  myObject,
  "a"
  // 讓 a 像普通屬性一樣可以枚舉 { enumerable: true, value: 2 }
);
Object.defineProperty(
  myObject,
  "b",
  // 讓b不可枚舉
  { enumerable: false, value: 3 }
);
myObject.b; // 3
"b" in myObject; // true
myObject.hasOwnProperty("b"); // true
// .......
for (var k in myObject) {
  console.log(k, myObject[k]); // "a" 2
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜗顽,隨后出現(xiàn)的幾起案子布卡,更是在濱河造成了極大的恐慌,老刑警劉巖雇盖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忿等,死亡現(xiàn)場離奇詭異,居然都是意外死亡崔挖,警方通過查閱死者的電腦和手機贸街,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狸相,“玉大人薛匪,你說我怎么就攤上這事∨Ь椋” “怎么了逸尖?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瘸右。 經(jīng)常有香客問我娇跟,道長,這世上最難降的妖魔是什么尊浓? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任逞频,我火速辦了婚禮,結(jié)果婚禮上栋齿,老公的妹妹穿的比我還像新娘苗胀。我一直安慰自己,他們只是感情好瓦堵,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布基协。 她就那樣靜靜地躺著,像睡著了一般菇用。 火紅的嫁衣襯著肌膚如雪澜驮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天惋鸥,我揣著相機與錄音杂穷,去河邊找鬼。 笑死卦绣,一個胖子當著我的面吹牛耐量,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滤港,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼廊蜒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起山叮,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤著榴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后屁倔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脑又,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年汰现,在試婚紗的時候發(fā)現(xiàn)自己被綠了挂谍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞎饲,死狀恐怖口叙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嗅战,我是刑警寧澤妄田,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站驮捍,受9級特大地震影響疟呐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜东且,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一启具、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧珊泳,春花似錦鲁冯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秧了,卻和暖如春跨扮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留婉商,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓钧汹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親录择。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容