前言
目前的前端世界,三大框架橫行,原生JavaScript所用越來越少,但我認為JavaScript作為每一個前端工程師的立身之本,學再多遍都不為過,
因此我決定整理JavaScript中容易忽視或者混淆的知識點,寫一系列文章,以靈魂拷問的方式,系統且完整的帶大家遨游基礎的的JavaScript,給大家帶來不一樣的體驗,
系列文章鏈接:
- 【面試利器】原生JS靈魂拷問,你能答上多少(一)
- 【面試利器】原生JS靈魂拷問,你能答上多少(二)
JavaScript陣列之問
第二十七問:那些陣列方法會修改原陣列嗎?那些不會?
這是一個比較重要的考點,我在看牛客的前端面試題時,該問題反復出現,
- 改變自身值的方法(共9個)
pop push shift unshift splice sort reverse
// ES6新增
copyWithin
fill
- 不改變自身值的方法
join // 回傳字串
forEach // 無回傳值
concat map filter slice // 回傳新陣列
every some // 回傳布林值
reduce // 不一定回傳什么
indexOf lastIndexOf // 回傳數值(索引值)
// ES6新增
find findIndex
第二十八問:shift 與 unshift 的回傳值是什么?
unshift()在陣列開始處插入元素,shift()洗掉陣列第一個元素- 關于兩者回傳值,直接上測驗代碼:
const arr = [1,2,3,4,5]
undefined
console.log(arr.unshift(0)) // 6
console.log(arr.shift()) // 0
由上述代碼可知:unshift回傳插入元素后的新陣列元素個數,shift回傳洗掉的元素值,
那么可以類比推理一下,push回傳的是插入元素的新陣列元素格式,pop回傳洗掉的元素值,
第二十九問:new Array() 與 Array.of() 的區別是什么?
Array.of()方法創建一個具有可變數量引數的新陣列實體,而不考慮引數的數量或型別,這好像跟new Array的功能極度接近,為什么ES6添加了新方法那?
原因就在于 new Array(n) ,只有一個引數時,構造的并非只含 n 的陣列,而是 1*n 的陣列,值全為 undefined,具體看測驗:
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
第三十問:你能靈活運用 splice() 方法嗎?splice() 的回傳值是什么?
- 基本使用
語法:
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
引數:
start?: 指定修改的開始位置(從0計數)deleteCount(可選): 整數,表示要移除的陣列元素的個數,item1, item2...(可選): 添加到陣列的元素
回傳值:由被洗掉的元素組成的一個陣列,沒有洗掉回傳空陣列,
2. 陣列中插入元素
陣列中只提供了在陣列頭尾插入與洗掉的方法,沒有提供在中間位置插入的方法,但可以通過將 deleteCount 設定為0,來實作插入效果,
const arr = [1,2,3,4,5]
console.log(arr.splice(2,0,100)) // []
console.log(arr) // [1, 2, 100, 3, 4, 5]
第三十一問:splice() 洗掉元素時有沒有要注意的地方?
大家看到這個問題,可能會感覺有幾分蒙,沒遇到過應該比較難想到,我來假設一個場景大家就懂了,
現在有這樣一個陣列 [1,-1,2,-1,-5],我想洗掉掉所有的負數,于是我就寫出了下面的代碼:
const arr = [1,-1,-2,1,-5]
for(let i = 0; i<arr.length; i++) {
if (arr[i] < 0) {
arr.splice(i, 1)
}
}
console.log(arr) // [1, -2, 1]
這就奇怪了,為啥 -2 沒被洗掉掉?我在上面的代碼加一句列印,大家應該就懂了,
for(let i = 0; i<arr.length; i++) {
if (arr[i] < 0) {
arr.splice(i, 1)
console.log(a[i]) // -2 undefined
}
}
陣列洗掉 -1 之后,當前的 i 值為 -2,此次遍歷結束,i++,正好空過了 -2 元素,因此如果splice洗掉元素發生在陣列中,一定要注意回呼i的位置,
第三十二問:你會使用 reduce() 嗎?
- 累加累乘
function Accumulation(...vals) {
return vals.reduce((t, v) => t + v, 0);
}
function Multiplication(...vals) {
return vals.reduce((t, v) => t * v, 1);
}
- 代替
reverse
function Reverse(arr = []) {
return arr.reduceRight((t, v) => (t.push(v), t), []);
}
- 陣列扁平化
function Flat(arr = []) {
return arr.reduce((t, v) => t.concat(Array.isArray(v) ? Flat(v) : v), [])
}
- 反轉字串
[..."hello world"].reduce((a, v) => v + a)
[..."hello world"].reverse().join('')
- 驗證括號是否合法
// 這是一個十分巧妙的用法,如果結果等于0說明,括號數量是合法的,
[..."(())()(()())"].reduce((a,i)=> i === '(' ? a+1 : a-1 , 0);
第三十三問:map、filter、every、some 如何使用?
map(): 創建一個新陣列,其結果是該陣列中的每個元素是呼叫一次提供的函式后的回傳值,其中原始陣列不會發生改變,
// 回傳numbers的平方根
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt); // [1, 2, 3]
filter(): 創建一個新陣列, 其包含通過所提供函式實作的測驗的所有元素,
// 篩選排除所有較小的值
function isBigEnough(element) {
return element >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough); // filtered is [12, 130, 44]
-
some:用于檢測陣列中的元素是否滿足指定條件,- 如果有一個元素滿足條件,則運算式回傳
true, 剩余的元素不會再執行檢測 - 如果沒有滿足條件的元素,則回傳
false
- 如果有一個元素滿足條件,則運算式回傳
// 檢測在陣列中是否有元素大于 10
function isBiggerThan10(element, index, array) {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
-
every: 檢測陣列所有元素是否都符合指定條件.- 如果陣列中檢測到有一個元素不滿足,則整個運算式回傳
false,且剩余的元素不會再進行檢測 - 如果所有元素都滿足條件,則回傳
true
- 如果陣列中檢測到有一個元素不滿足,則整個運算式回傳
// 檢測在陣列中元素是否都大于 10
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
第三十四問:陣列有多少種遍歷方式,各自的效率如何?
第三十五問:如何實作陣列亂序?
Math.random
提到亂序,大家首先會想到使用 Math.random,比如下面代碼:
var values = [1, 2, 3, 4, 5];
values.sort(function(){
return Math.random() - 0.5;
});
console.log(values)
Math.random() - 0.5 隨機得到一個正數,負數或者0,之后通過sort實作亂序,
但這種方法的效果其實并不如人意,具體測驗可參考博客:JavaScript專題之亂序
Fisher–Yates洗牌演算法
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
return a;
}
第三十六問:你知道多少種陣列去重的方法嗎?
- 解法一:使用雙重
for和splice
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){
//第一個等同于第二個,splice方法洗掉第二個
arr.splice(j,1);
// 洗掉后注意回呼j
j--;
}
}
}
return arr;
}
- 使用
indexOf或includes加新陣列
//使用indexof
function unique(arr) {
var uniqueArr = []; // 新陣列
for (let i = 0; i < arr.length; i++) {
if (uniqueArr.indexOf(arr[i]) === -1) {
//indexof回傳-1表示在新陣列中不存在該元素
uniqueArr.push(arr[i])//是新陣列里沒有的元素就push入
}
}
return uniqueArr;
}
// 使用includes
function unique(arr) {
var uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
//includes 檢測陣列是否有某個值
if (!uniqueArr.includes(arr[i])) {
uniqueArr.push(arr[i])//
}
}
return uniqueArr;
}
sort排序后,使用快慢指標的思想
function unique(arr) {
arr.sort((a, b) => a - b);
var slow = 1,
fast = 1;
while (fast < arr.length) {
if (arr[fast] != arr[fast - 1]) {
arr[slow ++] = arr[fast];
}
++ fast;
}
arr.length = slow;
return arr;
}
sort 方法用于從小到大排序(回傳一個新陣列),其引數中不帶以上回呼函式就會在兩位數及以上時出現排序錯誤(如果省略,元素按照轉換為的字串的各個字符的 Unicode 位點進行排序,兩位數會變為長度為二的字串來計算),
4. ES6 提供的 Set 去重
function unique(arr) {
const result = new Set(arr);
return [...result];
//使用擴展運算子將Set資料結構轉為陣列
}
Set 中的元素只會出現一次,即 Set 中的元素是唯一的,
5. 使用哈希表存盤元素是否出現(ES6 提供的 map)
function unique(arr) {
let map = new Map();
let uniqueArr = new Array(); // 陣列用于回傳結果
for (let i = 0; i < arr.length; i++) {
if(map.has(arr[i])) { // 如果有該key值
map.set(arr[i], true);
} else {
map.set(arr[i], false); // 如果沒有該key值
uniqueArr.push(arr[i]);
}
}
return uniqueArr ;
}
map 物件保存鍵值對,與物件類似,但 map 的鍵可以是任意型別,物件的鍵只能是字串型別,
如果陣列中只有數字也可以使用普通物件作為哈希表,
6. filter 配合 indexOf
function unique(arr) {
return arr.filter(function (item, index, arr) {
//當前元素,在原始陣列中的第一個索引==當前索引值,否則回傳當前元素
//不是那么就證明是重復項,就舍棄
return arr.indexOf(item) === index;
})
}
這里有可能存在疑問,我來舉個例子:
const arr = [1,1,2,1,3]
arr.indexOf(arr[0]) === 0 // 1 的第一次出現
arr.indexOf(arr[1]) !== 1 // 說明前面曾經出現過1
reduce配合includes
function unique(arr){
let uniqueArr = arr.reduce((acc,cur)=>{
if(!acc.includes(cur)){
acc.push(cur);
}
return acc;
},[]) // []作為回呼函式的第一個引數的初始值
return uniqueArr
}
第三十七問:你知道類陣列如何轉化為陣列嗎?
Array.prototype.slice.call()
const arrayLike = {
0: '111',
1: '222',
length: 2
}
console.log(Array.prototype.slice.call(arrayLike)) // ["1", "2"]
Array.from()
Array.from 是 ES6 新增的方法,它可以將**類陣列物件和可遍歷(iterable)**轉變為真正的陣列,
const arrayLike = {
0: '1',
1: '2',
length: 2
}
console.log(Array.from(arrayLike)) // ["1", "2"]
(...)擴展運算子
擴展運算子呼叫的是遍歷器介面,如果一個物件沒有部署此介面就無法完成轉換,
上面咱們自己寫的普通類陣列就無法使用…運算子,
const arrayLike = {
0: '1',
1: '2',
length: 2
}
// Uncaught TypeError: arrayLike is not iterable
console.log([...arrayLike]) // ["1", "2"]
如果部署了遍歷器介面,例如 arguments 類陣列,便可以使用擴展運算子,
function fn() {
console.log([...arguments])
}
fn(1,2,3) // [1, 2, 3]
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/333823.html
標籤:其他
上一篇:Vue中diff演算法詳解
