1. 程式人生 > 其它 >cmake字串轉陣列_JS 陣列中你或許不知道的操作

cmake字串轉陣列_JS 陣列中你或許不知道的操作

技術標籤:cmake字串轉陣列

JS 中的 Array

ecma-262 中的定義:Array 物件是一種特殊物件,它會對陣列索引屬性鍵進行特殊處理。 每個Array 物件都有一個不可配置的length屬性,其最大值是2³²- 1

Array(len)

當且僅當使用一個引數呼叫 Array 建構函式時,此描述才適用。

執行過程:

1.定義 numberOfArgs 為傳遞給此函式的呼叫的實引數量; 2.斷言:numberOfArgs 為1; 3.如果 NewTargetundefined ,就設定 newTarget活動函式物件 ,並且讓 newTarget

成為 NewTarget ; 4.原型 proto通過原生方法來構造; 5.然後定義arrayArrayCreate(0,proto); 6.如果 len 的型別不是個Number,則: a.定義 defineStatusCreateDataProperty(array, "0", len); b.斷言:defineStatus為真; c.讓 intLen(初始化長度) 為 1。

7.或者: a.定義intLenToUint32(len)

b.如果intLen不等於len,丟擲RangeError異常。

8.執行Set(array, "length", intLen, true)(原生方法,給物件的屬性賦值)

; 9.返回array

Array(...items)

當且僅當使用至少兩個引數呼叫Array建構函式時,此描述才適用。

執行過程:

1.定義 numberOfArgs 為傳遞給此函式的呼叫的實引數量; 2.斷言: numberOfArgs 大於等於2; 3.如果 NewTargetundefined ,就設定 newTarget活動函式物件 ,並且讓 newTarget 成為 NewTarget ; 4.原型 proto通過原生方法來構造; 5.然後定義arrayArrayCreate(numberOfArgs,proto); 6.定義 k 為 0; 7.定義 items

為 正序傳入引數的 零源(zero-origined) 列表; 8.重複,當 k 小於 numberOfArgs a.定義 PkToSting(k); b.定義 itemKitem[k]; c.定義 defineStatusCreateDataProperty(array, Pk, itemK); d.斷言:defineStatus為真; e.k 加1。 9.斷言: arraylength 值為 numberOfArgs; 10.返回 array

empty

以上便是構造Array()時不同情況的具體實現。但是我們從上面的斷言可以知道,構造結果有可能為真,有可能為假。還有是定義指定長度陣列時會出現什麼事呢?

V8原始碼Array 有個CloneElementAt的方法。定義如下:

在指定索引處克隆元素時,如果克隆失敗,則返回一個空控制代碼(任何原因)。

從個定義可知,當我們構造一個指定長度的 Array 時,由於有長度,所以會開闢相應下標的空間,但是因為該下標並沒有元素,所以就會返回empty,任何原因構造陣列元素失敗時,都會返回一個empty

示例如下:

var arr = new Array(10);

arr // [empty × 10]

以上總結

上面是 ECMA 上的定義以及 V8 原始碼的容錯處理,其實簡單來說就是:

呼叫 Array(args) 時:

  1. 用原生方法生成原型proto

  2. 判斷args的型別;

  3. 如果為undefined,則直接返回建立陣列的原生方法ArrayCreate

  4. 如果為number,則用原生方法Set建立args長度的陣列,並通過原生方法CloneElementAt來建立argsempty作為陣列元素,如果args大於2³²- 1的話,會報錯;

  5. 如果為其他型別,則把args變成陣列元素,並用 原生方法CreateDataProperty建立引數,然後返回建立陣列的原生方法ArrayCreate

型別轉換

型別轉換是一個經常出現在一些網上常見面試題或者奇技淫巧中的內容。那麼關於陣列的型別轉換,又是怎樣的呢?

首先我們要知道,在 JS 中型別轉換隻有三種情況,分別是:

  • 轉換為布林值

  • 轉換為數字

  • 轉換為字串

轉換為原始型別

物件在轉換型別的時候,會執行原生方法ToPrimitive

其演算法如下:

  1. 如果已經是原始型別,則返回當前值;

  2. 如果需要轉字串則先呼叫toSting方法,如果此時是原始型別則直接返回,否則再呼叫valueOf方法並返回結果;

  3. 如果不是字串,則先呼叫valueOf方法,如果此時是原始型別則直接返回,否則再呼叫toString方法並返回結果;

  4. 如果都沒有原始型別返回,則丟擲TypeError型別錯誤。

當然,我們可以通過重寫 Symbol.toPrimitive來制定轉換規則,此方法在轉原始型別時呼叫優先順序最高。

// 下面例子來自YCK的小冊

const data = {

valueOf () {

return 1;

},

toString () {

return '1';

},

[Symbol.toPrimitive]() {

return 2;

}

};

data + 1 // 3

轉換為布林值

物件轉換為布林值的規則如下表:

引數型別結果
Undefined返回false
Null返回false
Boolean返回 當前引數。
Number如果引數為+0-0NaN,則返回false;其他情況則返回true
String如果引數為空字串,則返回false;否則返回true
Symbol返回true
Object返回true

轉換為數字

物件轉換為數字的規則如下表:

引數型別結果
Undefined返回NaN
NullReturn +0.
Boolean如果引數為true,則返回1false則返回+0
Number返回當前引數。
String先呼叫ToPrimitive,再呼叫ToNumber,然後返回結果。
Symbol丟擲TypeError錯誤。
Object先呼叫ToPrimitive,再呼叫ToNumber,然後返回結果。

轉換為字串

物件轉換為字串的規則如下表:

引數型別結果
Undefined返回"undefined"
Null返回"null"
Boolean如果引數為true,則返回"true";否則返回"false"
Number呼叫NumberToString,然後返回結果。
String返回 當前引數。
Symbol丟擲TypeError錯誤。
Object先呼叫ToPrimitive,再呼叫ToString,然後返回結果。

陣列的型別轉換

所以通過上面的轉換規則,我們是否能夠輕鬆地看懂以下的隱式轉換呢?

[1,2,3] + {a: 1, b: 2} // "1,2,3[object Object]"

[1,2,3] + 1 // "1,2,31"

[1,2,3] + true // "1,2,3true"

[1,2,3] + undefined // "1,2,3undefined"

[1,2,3] + null // "1,2,3null"

[1,2,3] + '123' // "1,2,3123"

[1,2,3] + Symbol('biu') // "Uncaught TypeError"

所以各位是否理解上述隱式轉換的答案呢?

關於 API 使用的一些經驗與思考

JS陣列自帶了很多的方法,在現代工程化資料驅動的理念下,這些方法都是非常重要的。

loops

forEachArray 方法中比較基本的一個,作用也很簡單,與 for,就是遍歷,迴圈。不同的是, forEach可以選擇自定義上下文環境。例子如下:

var arr1 = [1, 2, 3];

var arr2 = [5, 6, 7];

arr1.forEach(function (e, i, a) {

console.log(e, this); // this 為 arr2

}, arr2);

但是如果 forEach的回撥函式是用箭頭函式定義的,那麼就無法改變它原本指向的上下文環境。例子如下:

var arr1 = [1, 2, 3];

var arr2 = [5, 6, 7];

arr1.forEach((e, i, a) => {

console.log(e, this); // this為window物件

}, arr2);

所以如果我們要實現以下這個功能:

class="1">1

class="2">2

class="3">3

class="4">4

class="5">5

'use strict';

var ul = document.querySelector('ul');

ul.onClick = event => {

var cls = event.target.className;

ul.querySelectorAll('li').forEach(el => {

el.style.color = (cls === el.className ? '#FFF' : '#FF0');

});

};

在ES6以前的環境中,如果直接用 for迴圈,會出現只能獲取到最後一個元素的問題,但是用 forEach則沒有這個問題。

reduce

Array ES5 API reducereduceRight,可以輕鬆實現並歸元素的功能,例子如下:

如果我們需要實現一個這樣的物件:

{

a: 1,

b: 2,

c: 3

...

};

那麼用reduce就會變得很簡單:

var newArr = 'a,b,c,d,e,f'.split(',').reduce((acc, cur, idx) => {

let o = {};

if (Object.prototype.toString.call(acc) !== '[object Object]') {

o[cur] = idx;

} else {

let newO = {};

newO[cur] = idx;

o = {

...acc,

...newO,

};

};

return o;

}, 'a');

效能

上面演示了通過JS陣列API實現的一些功能,所以與 for迴圈比效能如何呢?

var arr = new Array(100);

arr.forEach(data => {

console.log(data);

});

for (var i = 0; i < arr.length; ++i) {

console.log(arr[i]);

};

所以哪個更耗時間呢?

網上一直流傳著for 迴圈效能比 forEach 效能好,考慮效能少用 forEach的言論,其實以前的瀏覽器的確是這種情況。

現如今(2019)結果又會是如何呢?

以下程式碼測試環境為:Chrome 73.0.3683 / Windows 10 0.0.0

28b9ae1d46f2281d19996e754d7dfd90.png

現代的瀏覽器效能優化已經做得比以前好很多了,再加上電子裝置本身的硬體也越來越好,所以程式碼塊的效能不是我們首要的考慮因素。我們應該優化的是我們表示式是否清晰明瞭,是否適合後期維護或拓展。