本文目錄:
- 1.手寫reduce
- 2.手寫map
- 3.手寫filter
- 4.手寫防抖
- 5.手寫節(jié)流
- 6.實現(xiàn)flat
- 7.手寫深拷貝
- 8.手寫Promise
1.手寫reduce
reduce 接收兩個參數(shù):
第一個參數(shù)是在每一項上調(diào)用的函數(shù)
該函數(shù)接收 4 個參數(shù):
1.前一個值 prev
2.當(dāng)前值 cur
3.項的索引 index
4.數(shù)組對象 array
第二個可選參數(shù)是作為歸并基礎(chǔ)的初始值
reduce 方法返回一個最終的值
Array.prototype.myReduce = function (fn, initialValue) {
console.log(this)
let acc = initialValue || this[0];
const startIndex = initialValue ? 0 : 1;
for (let i = startIndex; i < this.length; i++) {
acc = fn(acc, this[i], i, this);
}
return acc;
};
2.手寫map
map 迭代方法接收兩個參數(shù):
- 對每一項執(zhí)行的函數(shù)
- 該函數(shù)接收三個參數(shù):
- 數(shù)組項的值
- 數(shù)組項的下標(biāo)
- 數(shù)組對象本身
- 該函數(shù)接收三個參數(shù):
- 指定 this 的作用域?qū)ο?/li>
map 方法返回每次函數(shù)調(diào)用結(jié)果組成的數(shù)組。
代碼表示:
arr.map(function(item, index, arr) {}, this);
代碼實現(xiàn):
Array.prototype.myMap = function (fn, context) {
const array = this
context = Object(context) || global // 嚴(yán)格模式下
const resultArr = []
for (let i = 0; i < array.length; i++) {
let item = array[i]
result = fn.call(context, item, i, array)
resultArr.push(result)
}
return resultArr
};
注: 嚴(yán)格模式下,context 為 null 或 undefined 時 Object(context) 返回空對象,不會被賦值為global
3.手寫filter
filter 方法接收兩個參數(shù):
- 對每一項執(zhí)行的函數(shù)
- 該函數(shù)接收三個參數(shù):
- 數(shù)組項的值
- 數(shù)組項的下標(biāo)
- 數(shù)組對象本身
- 該函數(shù)接收三個參數(shù):
- 指定 this 的作用域?qū)ο?/li>
filter 方法返回 執(zhí)行結(jié)果為true的項組成的數(shù)組。
代碼表示:
arr.filter(function(item, index, arr){}, context)
代碼實現(xiàn):
Array.prototype.fakeFilter = function fakeFilter(fn, context) {
if (typeof fn !== "function") {
throw new TypeError(`${fn} is not a function`);
}
let arr = this;
let temp = [];
for (let i = 0; i < arr.length; i++) {
let result = fn.call(context, arr[i], i, arr);
if (result) temp.push(arr[i]);
}
return temp;
};
4.手寫防抖
函數(shù)防抖 是指在事件被觸發(fā) n 秒后再執(zhí)行回調(diào),如果在這 n 秒內(nèi)事件又被觸發(fā),則重新計時。這可以使用在一些點擊請求的事件上,避免因為用戶的多次點擊向后端發(fā)送多次請求。
通俗點講就是=>你點的再快都沒用(新的點擊會清除上一次的點擊效果),要特定時間之后才會觸發(fā)
代碼示例
let time2;
document.getElementById('防抖').onclick =
function () {
clearTimeout(time2);
time2=setTimeout(function () {
//do something***
},2000);
};
5.手寫節(jié)流
函數(shù)節(jié)流 是指規(guī)定一個單位時間,在這個單位時間內(nèi),只能有一次觸發(fā)事件的回調(diào)函數(shù)執(zhí)行,如果在同一個單位時間內(nèi)某事件被觸發(fā)多次,只有一次能生效。節(jié)流可以使用在 scroll 函數(shù)的事件監(jiān)聽上,通過事件節(jié)流來降低事件調(diào)用的頻率。
通俗點講就是=>點擊就可以觸發(fā)事件,但是特定時間內(nèi)只會觸發(fā)一次,(技能冷卻)
代碼示例
let bool=true;
document.getElementById('節(jié)流').onclick = function () {
if(bool){
//do something***
bool=false;
setTimeout(()=>{
bool=true
},2000)
}
}
6.實現(xiàn)flat
flat() 方法會按照一個可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個新數(shù)組返回。
定義一個數(shù)組ary以及將ary轉(zhuǎn)換為字符串str
let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);
直接調(diào)用flat方法實現(xiàn)多維數(shù)組的扁平化
let ary_flat = ary.flat(Infinity);
如果不用flat,第一種實現(xiàn)方法
ary = str.replace(/(\[\]))/g, '').split(',');
第二種實現(xiàn)方法
str = str.replace(/(\[\]))/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);
第三種實現(xiàn)方法
let result = [];
let myFlat = function (ary) {
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (Array.isArray(ary[i])) {
myFlat(item);
} else {
result.push(item);
}
}
return result;
};
第四種實現(xiàn)方法
while (ary.some(Array.isArray)) {
console.log('xxx')
ary = [].concat(...ary);
}
7.手寫深拷貝
我們在使用深拷貝的時候,一定要弄清楚我們對深拷貝的要求程度:是僅“深”拷貝第一層級的對象屬性或數(shù)組元素,還是遞歸拷貝所有層級的對象屬性和數(shù)組元素
怎么檢驗深拷貝成功
改變?nèi)我庖粋€新對象/數(shù)組中的屬性/元素, 都不改變原對象/數(shù)組
方法一:深拷貝的最簡單直接的方法:
乞丐版: JSON.parse(JSON.stringify(object)),缺點諸多(會忽略undefined、symbol、函數(shù);不能解決循環(huán)引用;不能處理正則、new Date())
方法二:手動寫遞歸
基礎(chǔ)版(面試夠用): 淺拷貝+遞歸 (只考慮了普通的 object和 array兩種數(shù)據(jù)類型)
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
function copy (obj) {
var newobj = obj.constructor === Array ? [] : {};
if(typeof obj !== 'object'){
return;
}
for(var i in obj){
newobj[i] = typeof obj[i] === 'object' ?
copy(obj[i]) : obj[i];
}
return newobj
}
var copyArray = copy(array)
copyArray[0].number = 100;
console.log(array); // [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]
上面這兩個深拷貝的方法最大缺點是都會丟失函數(shù)Function、正則表達(dá)式等特殊類型。
8.手寫Promise
簡單版本,理解其原理
function myPromise(executor) {
var _this = this;
this.onFulfilled = []; //成功的回調(diào)
this.onRejected = []; //失敗的回調(diào)
this.state = "PENDING"; //狀態(tài)
this.value = undefined; //成功結(jié)果
this.reason = undefined; //失敗原因
function resolve(value) {
if (_this.state === "PENDING") {
_this.state = "FULFILLED";
_this.value = value;
_this.onFulfilled.forEach((fn) => fn(value));
}
}
function reject(reason) {
if (_this.state === "PENDING") {
_this.state = "REJECTED";
_this.reason = reason;
_this.onRejected.forEach((fn) => fn(reason));
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 定義鏈?zhǔn)秸{(diào)用的then方法
myPromise.prototype.then = function (onFullfilled, onRejected) {
if (this.state === "FULFILLED") {
typeof onFulfilled === "function" && onFulfilled(this.value);
}
if (this.state === "REJECTED") {
typeof onRejected === "function" && onRejected(this.reason);
}
if (this.state === "PENDING") {
typeof onFulfilled === "function" &&
this.onFulfilled.push(onFulfilled);
typeof onRejected === "function" && this.onRejected.push(onRejected);
}
};
var myP = new myPromise((resolve, reject) => {
console.log("執(zhí)行");
setTimeout(() => {
reject(3);
}, 1000);
});
myP.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);