JavaScript 編程語言【 數據類型】過濾|排序|映射|迭代

文章目錄

      • 將 border-left-width 轉換成 borderLeftWidth
      • 過濾范圍
      • 原位(in place)過濾范圍
      • 降序排列
      • 復制和排序數組
      • 創建一個可擴展的 calculator
      • 映射到 names
      • 映射到對象
      • 按年齡對用戶排序
      • 隨機排列數組
      • 獲取平均年齡
      • 數組去重
      • 從數組創建鍵(值)對象
  • Iterable object(可迭代對象)
    • Symbol.iterator
    • 字符串是可迭代的
    • 顯式調用迭代器
    • 可迭代(iterable)和類數組(array-like)
    • Array.from
    • 總結

?任務

將 border-left-width 轉換成 borderLeftWidth

重要程度5??

編寫函數 camelize(str) 將諸如 “my-short-string” 之類的由短劃線分隔的單詞變成駱駝式的 “myShortString”。

即:刪除所有短橫線,并將短橫線后的每一個單詞的首字母變為大寫。

示例:

camelize("background-color") == 'backgroundColor';
camelize("list-style-image") == 'listStyleImage';
camelize("-webkit-transition") == 'WebkitTransition';

提示:使用 split 將字符串拆分成數組,對其進行轉換之后再 join 回來。

打開帶有測試的沙箱。

解決方案

function camelize(str) {
return str.split('-') // splits 'my-long-word' into array ['my', 'long', 'word'].map(// capitalizes first letters of all array items except the first one// converts ['my', 'long', 'word'] into ['my', 'Long', 'Word'](word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)).join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord'
}

使用沙箱的測試功能打開解決方案。

過濾范圍

重要程度4??

寫一個函數 filterRange(arr, a, b),該函數獲取一個數組 arr,在其中查找數值大于或等于 a,且小于或等于 b 的元素,并將結果以數組的形式返回。

該函數不應該修改原數組。它應該返回新的數組。

例如:

let arr = [5, 3, 8, 1];let filtered = filterRange(arr, 1, 4);alert( filtered ); // 3,1(匹配值)alert( arr ); // 5,3,8,1(未修改)

打開帶有測試的沙箱。

解決方案

function filterRange(arr, a, b) {
// 在表達式周圍添加了括號,以提高可讀性
return arr.filter(item => (a <= item && item <= b));
}let arr = [5, 3, 8, 1];let filtered = filterRange(arr, 1, 4);alert( filtered ); // 3,1(匹配的值)alert( arr ); // 5,3,8,1(未經改動的數組中的值)

使用沙箱的測試功能打開解決方案。

原位(in place)過濾范圍

重要程度4??

寫一個函數 filterRangeInPlace(arr, a, b),該函數獲取一個數組 arr,并刪除其中介于 ab 區間以外的所有值。檢查:a ≤ arr[i] ≤ b

該函數應該只修改數組。它不應該返回任何東西。

例如:

let arr = [5, 3, 8, 1];filterRangeInPlace(arr, 1, 4); // 刪除了范圍在 1 到 4 之外的所有值alert( arr ); // [3, 1]

打開帶有測試的沙箱。

解決方案

function filterRangeInPlace(arr, a, b) {for (let i = 0; i < arr.length; i++) {let val = arr[i];// 如果超出范圍,則刪除if (val < a || val > b) {arr.splice(i, 1);i--;}
}}let arr = [5, 3, 8, 1];filterRangeInPlace(arr, 1, 4); // 刪除 1 到 4 范圍之外的值alert( arr ); // [3, 1]

使用沙箱的測試功能打開解決方案。

降序排列

重要程度4??

let arr = [5, 2, 1, -10, 8];// ……你的代碼以降序對其進行排序alert( arr ); // 8, 5, 2, 1, -10

解決方案

let arr = [5, 2, 1, -10, 8];arr.sort((a, b) => b - a);alert( arr );

復制和排序數組

重要程度5??

我們有一個字符串數組 arr。我們希望有一個排序過的副本,但保持 arr 不變。

創建一個函數 copySorted(arr) 返回這樣一個副本。

let arr = ["HTML", "JavaScript", "CSS"];let sorted = copySorted(arr);alert( sorted ); // CSS, HTML, JavaScript
alert( arr ); // HTML, JavaScript, CSS (no changes)

解決方案

我們可以使用 slice() 來創建一個副本并對其進行排序:

function copySorted(arr) {
return arr.slice().sort();
}let arr = ["HTML", "JavaScript", "CSS"];let sorted = copySorted(arr);alert( sorted );
alert( arr );

創建一個可擴展的 calculator

重要程度5??

創建一個構造函數 Calculator,以創建“可擴展”的 calculator 對象。

該任務由兩部分組成。

  1. 首先,實現 calculate(str) 方法,該方法接受像 "1 + 2" 這樣格式為“數字 運算符 數字”(以空格分隔)的字符串,并返回結果。該方法需要能夠理解加號 + 和減號 -

    用法示例:

    let calc = new Calculator;alert( calc.calculate("3 + 7") ); // 10
    
  2. 然后添加方法 addMethod(name, func),該方法教 calculator 進行新操作。它需要運算符 name 和實現它的雙參數函數 func(a,b)

    例如,我們添加乘法 *,除法 / 和求冪 **

    let powerCalc = new Calculator;
    powerCalc.addMethod("*", (a, b) => a * b);
    powerCalc.addMethod("/", (a, b) => a / b);
    powerCalc.addMethod("**", (a, b) => a ** b);let result = powerCalc.calculate("2 ** 3");
    alert( result ); // 8
    
  • 此任務中沒有括號或復雜的表達式。
  • 數字和運算符之間只有一個空格。
  • 你可以自行選擇是否添加錯誤處理功能。

打開帶有測試的沙箱。

解決方案

  • 請注意方法的存儲方式。它們只是被添加到 this.methods 屬性中。
  • 所有檢測和數字轉換都通過 calculate 方法完成。將來可能會擴展它以支持更復雜的表達式。
function Calculator() {this.methods = {"-": (a, b) => a - b,"+": (a, b) => a + b};this.calculate = function(str) {let split = str.split(' '),a = +split[0],op = split[1],b = +split[2];if (!this.methods[op] || isNaN(a) || isNaN(b)) {return NaN;}return this.methods[op](a, b);};this.addMethod = function(name, func) {this.methods[name] = func;};
}

使用沙箱的測試功能打開解決方案。

映射到 names

重要程度5??

你有一個 user 對象數組,每個對象都有 user.name。編寫將其轉換為 names 數組的代碼。

例如:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };let users = [ john, pete, mary ];let names = /* ... your code */alert( names ); // John, Pete, Mary

解決方案

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };let users = [ john, pete, mary ];let names = users.map(item => item.name);alert( names ); // John, Pete, Mary

映射到對象

重要程度5??

你有一個 user 對象數組,每個對象都有 namesurnameid

編寫代碼以該數組為基礎,創建另一個具有 idfullName 的對象數組,其中 fullNamenamesurname 生成。

例如:

let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };let users = [ john, pete, mary ];let usersMapped = /* ... your code ... *//*
usersMapped = [{ fullName: "John Smith", id: 1 },{ fullName: "Pete Hunt", id: 2 },{ fullName: "Mary Key", id: 3 }
]
*/alert( usersMapped[0].id ) // 1
alert( usersMapped[0].fullName ) // John Smith

所以,實際上你需要將一個對象數組映射到另一個對象數組。在這兒嘗試使用箭頭函數 => 來編寫。

解決方案

let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };let users = [ john, pete, mary ];let usersMapped = users.map(user => ({
fullName: `${user.name} ${user.surname}`,
id: user.id
}));/*
usersMapped = [
{ fullName: "John Smith", id: 1 },
{ fullName: "Pete Hunt", id: 2 },
{ fullName: "Mary Key", id: 3 }
]
*/alert( usersMapped[0].id ); // 1
alert( usersMapped[0].fullName ); // John Smith

請注意,在箭頭函數中,我們需要使用額外的括號。

我們不能這樣寫:

let usersMapped = users.map(user => {
fullName: `${user.name} ${user.surname}`,
id: user.id
});

我們記得,有兩種箭頭函數的寫法:直接返回值 value => expr 和帶主體的 value => {...}

JavaScript 在這里會把 { 視為函數體的開始,而不是對象的開始。解決方法是將它們包裝在普通括號 () 中:

let usersMapped = users.map(user => ({
fullName: `${user.name} ${user.surname}`,
id: user.id
}));

這樣就可以了。

按年齡對用戶排序

重要程度5??

編寫函數 sortByAge(users) 獲得對象數組的 age 屬性,并根據 age 對這些對象數組進行排序。

例如:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };let arr = [ pete, john, mary ];sortByAge(arr);// now: [john, mary, pete]
alert(arr[0].name); // John
alert(arr[1].name); // Mary
alert(arr[2].name); // Pete

解決方案

function sortByAge(arr) {
arr.sort((a, b) => a.age - b.age);
}let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };let arr = [ pete, john, mary ];sortByAge(arr);// 排序后的數組為:[john, mary, pete]
alert(arr[0].name); // John
alert(arr[1].name); // Mary
alert(arr[2].name); // Pete

譯注:解決方案的代碼還可以更短一些

function sortByAge(arr) {
arr.sort((a, b) => a.age - b.age);
}

因為 sort() 方法的語法為 arr.sort([compareFunction]),如果沒有指明 compareFunction,那么元素會被按照轉換為的字符串的諸個字符的 Unicode 編碼進行排序,如果指明了 compareFunction,那么數組會按照調用該函數的返回值排序。即 ab 是兩個將要被比較的元素:

  • 如果 compareFunction(a, b) 小于 0,那么 a 會被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0,那么 ab 的相對位置不變。備注:ECMAScript 標準并不保證這一行為,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003 年之前的版本);
  • 如果 compareFunction(a, b) 大于 0,那么 b 會被排列到 a 之前。

因此,升序排列的函數可以簡寫為:(a, b) => a.age - b.age

隨機排列數組

重要程度3??

編寫函數 shuffle(array) 來隨機排列數組的元素。

多次運行 shuffle 可能導致元素順序的不同。例如:

let arr = [1, 2, 3];shuffle(arr);
// arr = [3, 2, 1]shuffle(arr);
// arr = [2, 1, 3]shuffle(arr);
// arr = [3, 1, 2]
// ...

所有元素順序應該具有相等的概率。例如,可以將 [1,2,3] 重新排序為 [1,2,3][1,3,2][3,1,2] 等,每種情況的概率相等。

解決方案

簡單的解決方案可以是:

function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}let arr = [1, 2, 3];
shuffle(arr);
alert(arr);

這樣是可以的,因為 Math.random() - 0.5 是一個可能是正數或負數的隨機數,因此排序函數會隨機地對數組中的元素進行重新排序。

但是,由于排序函數并非旨在以這種方式使用,因此并非所有的排列都具有相同的概率。

例如,請考慮下面的代碼。它運行 100 萬次 shuffle 并計算所有可能結果的出現次數:

function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}// 所有可能排列的出現次數
let count = {
'123': 0,
'132': 0,
'213': 0,
'231': 0,
'321': 0,
'312': 0
};for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}// 顯示所有可能排列的出現次數
for (let key in count) {
alert(`${key}: ${count[key]}`);
}

示例結果(取決于 Javascript 引擎):

123: 250706
132: 124425
213: 249618
231: 124880
312: 125148
321: 125223

我們可以清楚地看到這種傾斜:123213 的出現頻率比其他情況高得多。

使用不同的 JavaScript 引擎運行這個示例代碼得到的結果可能會有所不同,但是我們已經可以看到這種方法是不可靠的。

為什么它不起作用?一般來說,sort 是一個“黑匣子”:我們將一個數組和一個比較函數放入其中,并期望其對數組進行排序。但是由于比較的完全隨機性,這個黑匣子瘋了,它發瘋地確切程度取決于引擎中的具體實現方法。

還有其他很好的方法可以完成這項任務。例如,有一個很棒的算法叫作 Fisher-Yates shuffle。其思路是:逆向遍歷數組,并將每個元素與其前面的隨機的一個元素互換位置:

function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {let j = Math.floor(Math.random() * (i + 1)); // 從 0 到 i 的隨機索引// 交換元素 array[i] 和 array[j]// 我們使用“解構分配(destructuring assignment)”語法來實現它// 你將在后面的章節中找到有關該語法的更多詳細信息// 可以寫成:// let t = array[i]; array[i] = array[j]; array[j] = t[array[i], array[j]] = [array[j], array[i]];
}
}

讓我們以相同的方式測試一下:

function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {let j = Math.floor(Math.random() * (i + 1));[array[i], array[j]] = [array[j], array[i]];
}
}// 所有可能排列的出現次數
let count = {
'123': 0,
'132': 0,
'213': 0,
'231': 0,
'321': 0,
'312': 0
};for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}// 顯示所有可能排列的出現次數
for (let key in count) {
alert(`${key}: ${count[key]}`);
}

示例輸出:

123: 166693
132: 166647
213: 166628
231: 167517
312: 166199
321: 166316

現在看起來不錯:所有排列都以相同的概率出現。

另外,在性能方面,Fisher — Yates 算法要好得多,沒有“排序”開銷。

獲取平均年齡

重要程度4??

編寫 getAverageAge(users) 函數,該函數獲取一個具有 age 屬性的對象數組,并返回平均年齡。

平均值的計算公式是 (age1 + age2 + ... + ageN) / N

例如:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };let arr = [ john, pete, mary ];alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28

解決方案

function getAverageAge(users) {
return users.reduce((prev, user) => prev + user.age, 0) / users.length;
}let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };let arr = [ john, pete, mary ];alert( getAverageAge(arr) ); // 28

數組去重

重要程度4??

arr 是一個數組。

創建一個函數 unique(arr),返回去除重復元素后的數組 arr

例如:

function unique(arr) {/* your code */
}let strings = ["Hare", "Krishna", "Hare", "Krishna","Krishna", "Krishna", "Hare", "Hare", ":-O"
];alert( unique(strings) ); // Hare, Krishna, :-O

打開帶有測試的沙箱。

解決方案

讓我們先遍歷數字:

  • 對于每個元素,我們將檢查結果數組是否已經有該元素。
  • 如果有,則忽略,否則將其添加到結果中。
function unique(arr) {let result = [];for (let str of arr) {if (!result.includes(str)) {result.push(str);}}return result;
}let strings = ["Hare", "Krishna", "Hare", "Krishna","Krishna", "Krishna", "Hare", "Hare", ":-O"
];alert( unique(strings) ); // Hare, Krishna, :-O

代碼有效,但其中存在潛在的性能問題。

方法 result.includes(str) 在內部遍歷數組 result,并將每個元素與 str 進行比較以找到匹配項。

所以如果 result 中有 100 個元素,并且沒有任何一項與 str 匹配,那么它將遍歷整個 result 并進行 100 次比較。如果 result 很大,比如 10000,那么就會有 10000 次的比較。

這本身并不是問題,因為 JavaScript 引擎速度非常快,所以遍歷一個有 10000 個元素的數組只需要幾微秒。

但是我們在 for循環中對 arr 的每個元素都進行了一次檢測。

因此,如果 arr.length10000,我們會有 10000 * 10000 = 1 億次的比較。那真的太多了。

所以該解決方案僅適用于小型數組。

進一步,在后面的 【Map and Set(映射和集合)】一章中,我們將看到如何對該方法進行優化。

使用沙箱的測試功能打開解決方案。

從數組創建鍵(值)對象

重要程度4??

假設我們收到了一個用戶數組,形式為:{id:..., name:..., age:... }

創建一個函數 groupById(arr) 從該數組創建對象,以 id 為鍵(key),數組項為值。

例如:

let users = [{id: 'john', name: "John Smith", age: 20},{id: 'ann', name: "Ann Smith", age: 24},{id: 'pete', name: "Pete Peterson", age: 31},
];let usersById = groupById(users);/*
// 調用函數后,我們應該得到:usersById = {john: {id: 'john', name: "John Smith", age: 20},ann: {id: 'ann', name: "Ann Smith", age: 24},pete: {id: 'pete', name: "Pete Peterson", age: 31},
}
*/

處理服務端數據時,這個函數很有用。

在這個任務里我們假設 id 是唯一的。沒有兩個具有相同 id 的數組項。

請在解決方案中使用數組的 .reduce 方法。

打開帶有測試的沙箱。

解決方案

function groupById(array) {
return array.reduce((obj, value) => {obj[value.id] = value;return obj;
}, {})
}

打開帶有測試的沙箱。

Iterable object(可迭代對象)

可迭代(Iterable) 對象是數組的泛化。這個概念是說任何對象都可以被定制為可在 for..of 循環中使用的對象。

數組是可迭代的。但不僅僅是數組。很多其他內建對象也都是可迭代的。例如字符串也是可迭代的。

如果從技術上講,對象不是數組,而是表示某物的集合(列表,集合),for..of 是一個能夠遍歷它的很好的語法,因此,讓我們來看看如何使其發揮作用。

Symbol.iterator

通過自己創建一個對象,我們就可以輕松地掌握可迭代的概念。

例如,我們有一個對象,它并不是數組,但是看上去很適合使用 for..of 循環。

比如一個 range 對象,它代表了一個數字區間:

let range = {from: 1,to: 5
};// 我們希望 for..of 這樣運行:
// for(let num of range) ... num=1,2,3,4,5

為了讓 range 對象可迭代(也就讓 for..of 可以運行)我們需要為對象添加一個名為 Symbol.iterator 的方法(一個專門用于使對象可迭代的內建 symbol)。

  1. for..of 循環啟動時,它會調用這個方法(如果沒找到,就會報錯)。這個方法必須返回一個 迭代器(iterator) —— 一個有 next 方法的對象。
  2. 從此開始,for..of 僅適用于這個被返回的對象
  3. for..of 循環希望取得下一個數值,它就調用這個對象的 next() 方法。
  4. next() 方法返回的結果的格式必須是 {done: Boolean, value: any},當 done=true 時,表示循環結束,否則 value 是下一個值。

這是帶有注釋的 range 的完整實現:

let range = {from: 1,to: 5
};// 1. for..of 調用首先會調用這個:
range[Symbol.iterator] = function() {// ……它返回迭代器對象(iterator object):// 2. 接下來,for..of 僅與下面的迭代器對象一起工作,要求它提供下一個值return {current: this.from,last: this.to,// 3. next() 在 for..of 的每一輪循環迭代中被調用next() {// 4. 它將會返回 {done:.., value :...} 格式的對象if (this.current <= this.last) {return { done: false, value: this.current++ };} else {return { done: true };}}};
};// 現在它可以運行了!
for (let num of range) {alert(num); // 1, 然后是 2, 3, 4, 5
}

請注意可迭代對象的核心功能:關注點分離。

  • range 自身沒有 next() 方法。
  • 相反,是通過調用 range[Symbol.iterator]() 創建了另一個對象,即所謂的“迭代器”對象,并且它的 next 會為迭代生成值。

因此,迭代器對象和與其進行迭代的對象是分開的。

從技術上說,我們可以將它們合并,并使用 range 自身作為迭代器來簡化代碼。

就像這樣:

let range = {from: 1,to: 5,[Symbol.iterator]() {this.current = this.from;return this;},next() {if (this.current <= this.to) {return { done: false, value: this.current++ };} else {return { done: true };}}
};for (let num of range) {alert(num); // 1, 然后是 2, 3, 4, 5
}

現在 range[Symbol.iterator]() 返回的是 range 對象自身:它包括了必需的 next() 方法,并通過 this.current 記憶了當前的迭代進程。這樣更短,對嗎?是的。有時這樣也可以。

但缺點是,現在不可能同時在對象上運行兩個 for..of 循環了:它們將共享迭代狀態,因為只有一個迭代器,即對象本身。但是兩個并行的 for..of 是很罕見的,即使在異步情況下。

??無窮迭代器(iterator)

無窮迭代器也是可能的。例如,將 range 設置為 range.to = Infinity,這時 range 則成為了無窮迭代器。或者我們可以創建一個可迭代對象,它生成一個無窮偽隨機數序列。也是可能的。

next 沒有什么限制,它可以返回越來越多的值,這是正常的。

當然,迭代這種對象的 for..of 循環將不會停止。但是我們可以通過使用 break 來停止它。

字符串是可迭代的

數組和字符串是使用最廣泛的內建可迭代對象。

對于一個字符串,for..of 遍歷它的每個字符:

for (let char of "test") {// 觸發 4 次,每個字符一次alert( char ); // t, then e, then s, then t
}

對于代理對(surrogate pairs),它也能正常工作!(譯注:這里的代理對也就指的是 UTF-16 的擴展字符)

let str = '𝒳😂';
for (let char of str) {alert( char ); // 𝒳,然后是 😂
}

顯式調用迭代器

為了更深層地了解底層知識,讓我們來看看如何顯式地使用迭代器。

我們將會采用與 for..of 完全相同的方式遍歷字符串,但使用的是直接調用。這段代碼創建了一個字符串迭代器,并“手動”從中獲取值。

let str = "Hello";// 和 for..of 做相同的事
// for (let char of str) alert(char);let iterator = str[Symbol.iterator]();while (true) {let result = iterator.next();if (result.done) break;alert(result.value); // 一個接一個地輸出字符
}

很少需要我們這樣做,但是比 for..of 給了我們更多的控制權。例如,我們可以拆分迭代過程:迭代一部分,然后停止,做一些其他處理,然后再恢復迭代。

可迭代(iterable)和類數組(array-like)

這兩個官方術語看起來差不多,但其實大不相同。請確保你能夠充分理解它們的含義,以免造成混淆。

  • Iterable 如上所述,是實現了 Symbol.iterator 方法的對象。
  • Array-like 是有索引和 length 屬性的對象,所以它們看起來很像數組。

當我們將 JavaScript 用于編寫在瀏覽器或任何其他環境中的實際任務時,我們可能會遇到可迭代對象或類數組對象,或兩者兼有。

例如,字符串即是可迭代的(for..of 對它們有效),又是類數組的(它們有數值索引和 length 屬性)。

但是一個可迭代對象也許不是類數組對象。反之亦然,類數組對象可能不可迭代。

例如,上面例子中的 range 是可迭代的,但并非類數組對象,因為它沒有索引屬性,也沒有 length 屬性。

下面這個對象則是類數組的,但是不可迭代:

let arrayLike = { // 有索引和 length 屬性 => 類數組對象0: "Hello",1: "World",length: 2
};// Error (no Symbol.iterator)
for (let item of arrayLike) {}

可迭代對象和類數組對象通常都 不是數組,它們沒有 pushpop 等方法。如果我們有一個這樣的對象,并想像數組那樣操作它,那就非常不方便。例如,我們想使用數組方法操作 range,應該如何實現呢?

Array.from

有一個全局方法 Array.from 可以接受一個可迭代或類數組的值,并從中獲取一個“真正的”數組。然后我們就可以對其調用數組方法了。

例如:

let arrayLike = {0: "Hello",1: "World",length: 2
};let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World(pop 方法有效)

(*) 行的 Array.from 方法接受對象,檢查它是一個可迭代對象或類數組對象,然后創建一個新數組,并將該對象的所有元素復制到這個新數組。

如果是可迭代對象,也是同樣:

// 假設 range 來自上文的例子中
let arr = Array.from(range);
alert(arr); // 1,2,3,4,5 (數組的 toString 轉化方法生效)

Array.from 的完整語法允許我們提供一個可選的“映射(mapping)”函數:

Array.from(obj[, mapFn, thisArg])

可選的第二個參數 mapFn 可以是一個函數,該函數會在對象中的元素被添加到數組前,被應用于每個元素,此外 thisArg 允許我們為該函數設置 this

例如:

// 假設 range 來自上文例子中// 求每個數的平方
let arr = Array.from(range, num => num * num);alert(arr); // 1,4,9,16,25

現在我們用 Array.from 將一個字符串轉換為單個字符的數組:

let str = '𝒳😂';// 將 str 拆分為字符數組
let chars = Array.from(str);alert(chars[0]); // 𝒳
alert(chars[1]); // 😂
alert(chars.length); // 2

str.split 方法不同,它依賴于字符串的可迭代特性。因此,就像 for..of 一樣,可以正確地處理代理對(surrogate pair)。(譯注:代理對也就是 UTF-16 擴展字符。)

技術上來講,它和下面這段代碼做的是相同的事:

let str = '𝒳😂';let chars = []; // Array.from 內部執行相同的循環
for (let char of str) {chars.push(char);
}alert(chars);

……但 Array.from 精簡很多。

我們甚至可以基于 Array.from 創建代理感知(surrogate-aware)的slice 方法(譯注:也就是能夠處理 UTF-16 擴展字符的 slice 方法):

function slice(str, start, end) {return Array.from(str).slice(start, end).join('');
}let str = '𝒳😂𩷶';alert( slice(str, 1, 3) ); // 😂𩷶// 原生方法不支持識別代理對(譯注:UTF-16 擴展字符)
alert( str.slice(1, 3) ); // 亂碼(兩個不同 UTF-16 擴展字符碎片拼接的結果)

總結

可以應用 for..of 的對象被稱為 可迭代的

  • 技術上來說,可迭代對象必須實現 Symbol.iterator 方法。
    • obj[Symbol.iterator]() 的結果被稱為 迭代器(iterator)。由它處理進一步的迭代過程。
    • 一個迭代器必須有 next() 方法,它返回一個 {done: Boolean, value: any} 對象,這里 done:true 表明迭代結束,否則 value 就是下一個值。
  • Symbol.iterator 方法會被 for..of 自動調用,但我們也可以直接調用它。
  • 內建的可迭代對象例如字符串和數組,都實現了 Symbol.iterator
  • 字符串迭代器能夠識別代理對(surrogate pair)。(譯注:代理對也就是 UTF-16 擴展字符。)

有索引屬性和 length 屬性的對象被稱為 類數組對象。這種對象可能還具有其他屬性和方法,但是沒有數組的內建方法。

如果我們仔細研究一下規范 —— 就會發現大多數內建方法都假設它們需要處理的是可迭代對象或者類數組對象,而不是“真正的”數組,因為這樣抽象度更高。

Array.from(obj[, mapFn, thisArg]) 將可迭代對象或類數組對象 obj 轉化為真正的數組 Array,然后我們就可以對它應用數組的方法。可選參數 mapFnthisArg 允許我們將函數應用到每個元素。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38906.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38906.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38906.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

掌握React與TypeScript:從零開始繪制中國地圖

最近我需要使用reactts繪制一個界面&#xff0c;里面需要以中國地圖的形式展示區塊鏈從2019-2024年這五年的備案以及注銷情況&#xff0c;所以研究了一下這方面的工作&#xff0c;初步有了一些成果&#xff0c;所以現在做一些分享&#xff0c;希望對大家有幫助&#xff01; 在這…

手把手搞定報名亞馬遜科技認證

引言 亞馬遜云科技認證考試為我們這些技術從業者提供了提升專業技能的機會。無論選擇線上還是線下考試&#xff0c;每種方式都有其獨特的優勢和挑戰。選擇合適的考試方式將幫助我們更好地展示自己的技術水平。以下是我對不同考試方式的優缺點介紹&#xff0c;以及各科目的考試…

【pytorch12】什么是梯度

說明 導數偏微分梯度 梯度&#xff1a;是一個向量&#xff0c;向量的每一個軸是每一個方向上的偏微分 梯度是有方向也有大小&#xff0c;梯度的方向代表函數在當前點的一個增長的方向&#xff0c;然后這個向量的長度代表了這個點增長的速率 藍色代表比較小的值&#xff0c;紅色…

七月論文審稿GPT第5版:拿我司七月的早期paper-7方面review數據集微調LLama 3

前言 llama 3出來后&#xff0c;為了通過paper-review的數據集微調3&#xff0c;有以下各種方式 不用任何框架 工具 技術&#xff0c;直接微調原生的llama 3&#xff0c;畢竟也有8k長度了 效果不期望有多高&#xff0c;純作為baseline通過PI&#xff0c;把llama 3的8K長度擴展…

基于Linux的云端垃圾分類助手

項目簡介 本項目旨在開發一個基于嵌入式系統的智能垃圾分類裝置。該裝置能夠通過串口通信、語音播報、網絡通信等多種方式&#xff0c;實現垃圾的自動識別和分類投放。系統采用多線程設計&#xff0c;確保各功能模塊高效并行工作。 項目功能 垃圾分類識別 系統使用攝像頭拍攝…

解密tar文件解壓的Java實現技術

解密tar文件解壓的Java實現技術 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 引言 在日常的軟件開發和系統管理中&#xff0c;經常會遇到需要解壓縮文件的…

代碼隨想三刷動態規劃篇5

代碼隨想三刷動態規劃篇5 377. 組合總和 Ⅳ題目代碼 57. 爬樓梯&#xff08;第八期模擬筆試&#xff09;題目代碼 322. 零錢兌換題目代碼 279. 完全平方數題目代碼 377. 組合總和 Ⅳ 題目 鏈接 代碼 class Solution {public int combinationSum4(int[] nums, int target) {…

SM2的簽名值byte數組與ASN.1互轉

ASN.1抽象語言標記(Abstract Syntax Notation One) ASN.1是一種 ISO/ITU-T 標準,描述了一種對數據進行表示、編碼、傳輸和解碼的數據格式,它提供了一整套正規的格式用于描述對象的結構。 一、該結構的應用場景 例如在做待簽名的數字信封時,數字信封使用ASN.1封裝,這個時…

MySQL-行級鎖(行鎖、間隙鎖、臨鍵鎖)

文章目錄 1、介紹2、查看意向鎖及行鎖的加鎖情況3、行鎖的演示3.1、普通的select語句&#xff0c;執行時&#xff0c;不會加鎖3.2、select * from stu where id 1 lock in share mode;3.3、共享鎖與共享鎖之間兼容。3.4、共享鎖與排他鎖之間互斥。3.5、排它鎖與排他鎖之間互斥3…

論文調研_Awesome-Binary-Similarity

0. 概述 對 Awesome-Binary-Similarity 中列出的論文進行調研,重點總結這些論文的研究動機與未來研究方向。 1. 調研內容 論文名稱發表時間發表期刊期刊等級研究單位BinaryAI: Binary Software Composition Analysis via Intelligent Binary Source Code Matching2024年ICSE…

每日一題---OJ題:分隔鏈表

片頭 嗨&#xff01;小伙伴們&#xff0c;大家好&#xff01;今天我們一起來看看這道題----分隔鏈表 emmmm&#xff0c;這道題&#xff0c;看描述應該不算太難&#xff0c;我們一起來畫一畫圖唄&#xff01; 題目讀懂了&#xff0c;那么如何破解這道題呢&#xff1f; 思路&…

microApp vue3+vite+ts 子應用接入改造

公司做了一個平臺,使用的是microApp,需要把現有的一些系統作為子系統改造一下接入這個平臺,所以下面我說的都是子應用的改造,vue2的改造比較簡單,主要的vue3+vite+ts的改造。 參考官網 1、設置跨應用支持 vite默認開啟跨域支持,不需要額外配置。 2、注冊卸載函數 // …

nand flash spec

nand flash簡介 nand flash是一種非易失性存儲器。它具有高存儲密度、低成本和高耐用性的特點。 nand flash的特性是非易失性&#xff0c;即在電源關閉的情況下&#xff0c;數據仍然保留。 nand flash的存儲單元由浮動柵極晶體管組成&#xff0c;每個存儲單元可以存儲一位或多…

短視頻世界對我溫柔以待:成都柏煜文化傳媒有限公司

短視頻世界對我溫柔以待 在繁忙的都市生活中&#xff0c;每個人都在為生活奔波&#xff0c;為夢想努力。而在這個快節奏的時代里&#xff0c;短視頻如同一股清流&#xff0c;以其獨特的魅力&#xff0c;為我帶來了片刻的寧靜與溫柔。它像是一個無聲的朋友&#xff0c;在我疲憊…

(必看圖文)Hadoop集群安裝及MapReduce應用(手把手詳解版)

前言 隨著大數據時代的到來&#xff0c;處理和分析海量數據已成為企業和科研機構不可或缺的能力。Hadoop&#xff0c;作為開源的分布式計算平臺&#xff0c;因其強大的數據處理能力和良好的可擴展性&#xff0c;成為大數據處理領域的佼佼者。本圖文教程旨在幫助讀者理解Hadoop集…

Mysql面試合集

概念 是一個開源的關系型數據庫。 數據庫事務及其特性 事務&#xff1a;是一系列的數據庫操作&#xff0c;是數據庫應用的基本邏輯單位。 事務特性&#xff1a; &#xff08;1&#xff09;原子性&#xff1a;即不可分割性&#xff0c;事務要么全部被執行&#xff0c;要么就…

代碼隨想錄1數組

1 二分查找 Leetcode704 1 [l,r]區間 l 0, r nums.length-1; while(l<r) 因為lr有意義 2 [l,r)區間 l 0, r nums.length; while(l<r) Leetcode35 class Solution {public int searchInsert(int[] nums, int target) {int l0,rnums.length;while(l<r){int m l(…

使用設計模式來增強你的 SpringBoot 開發

SpringBoot 是一個出色的框架&#xff0c;可以快速構建強大而高效的應用程序。但你是否知道設計模式可以將 SpringBoot 開發提升到一個新的水平&#xff1f; ? 設計模式的重要性&#xff1a;了解設計模式如何促進代碼的可重用性、可維護性和整體應用程序健康。 ? SpringBoot…

在Spring Data JPA中使用@Query注解

目錄 前言示例簡單示例只查詢部分字段&#xff0c;映射到一個實體類中只查詢部分字段時&#xff0c;也可以使用List<Object[]>接收返回值再復雜一些 前言 在以往寫過幾篇spring data jpa相關的文章&#xff0c;分別是 Spring Data JPA 使用JpaSpecificationExecutor實現…

python 筆試面試八股(自用版~)

1 解釋型和編譯型語言的區別 解釋是翻譯一句執行一句&#xff0c;更靈活&#xff0c;eg&#xff1a;python; 解釋成機器能理解的指令&#xff0c;而不是二進制碼 編譯是整個源程序編譯成機器可以直接執行的二進制可運行的程序&#xff0c;再運行這個程序 比如c 2 簡述下 Pyth…