最近開始重新閱讀JS的文章跟教學影片(網路上大推的Javascript: Understanding the weird parts),原因是想要把荒廢的JS基礎程式能力在練扎實一點,其中令我覺得學習到最多東西的部份就是JS的實作了!
因為在工作時,我們使用的工具很多時候網頁就有原生支援,或是我們會去使用像是lodash的套件,這樣的優點是開發上會很快速但是其實底下的原理我其實沒有實際去理解過,當然在正常情況下不知道底層實作對開發上是沒有影響的,就像是會開車的人也不需要懂車子架構的原理一樣,但是假如有天車子拋錨了,如果懂的如何換輪子來解決問題,相信對駕駛人來說一定不是壞事!所以秉持著這個心態,這篇文章就是要來紀錄我用JS來自己造輪子的過程!話不多說,下面就是我這次嘗試的實作:
部分實作在之前鐵人賽就有寫過更詳細介紹,有興趣的人可以去那裡看,這裡就會單純的放上程式碼。另外,這裡推薦讀者可以嘗試去自己寫看看,相信對你也是有幫助的!
Array method 實作
Array.prototype.map
Array.prototype.myMap = function (callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
Array.prototype.filter
Array.prototype.myFilter = function (callback) {
const resultArray = [];
for (let i = 0; i < this.length; i++) {
if (!!callback(this[i], i, this)) {
resultArray.push(this[index]);
}
}
return resultArray;
};
Array.prototype.reduce
Array.prototype.myReduce = function (callback, initialValue) {
let init = initialValue || this[0];
let index = initialValue ? 0 : 1;
for (let i = index; i < this.length; i++) {
init = callback(init, this[i], i, this);
}
return init;
};
Promise 相關實作
Promise.all
function promiseAll(values) {
if (!Array.isArray(values)) return;
return new Promise((resolve, reject) => {
let results = [];
let completed = 0;
values.forEach((value, index) => {
Promise.resolve(value)
.then((result) => {
results[index] = result; // 確保回傳的Promise中,value的順序是對的!
completed += 1;
//如果成功的話就會回傳resolve
if (completed == values.length) {
resolve(results);
}
})
.catch((err) => reject(err));
});
});
}
Promise.race
const PromiseRace = (values) => {
return new Promise((resolve, reject) => {
for (const item of values) {
//第一個完成的會直接回傳
Promise.resolve(item).then(resolve).catch(reject);
}
});
};
Promise
完整程式碼在下方的連結:
https://github.com/0529bill/promise-implementation/blob/master/simplePromise.js
優化method
https://0529bill.github.io/bywater-blog/ironMan2022/reactOptimization
debounce
function debounce(func, delay) {
let timeout = null;
return function (...args) {
let context = this;
//綁定在傳進來的func上
clearTimeout(timeout);
//清除掉前一個timeout
timeout = setTimeout(function () {
func.apply(context, args);
}, delay);
};
}
throttle
function throttle(func, delay) {
let inThrottle;
let timeout = null;
return function (...args) {
let context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
clearTimeout(timeout);
timeout = setTimeout(function () {
inThrottle = false;
}, delay);
}
};
}
其他method
string轉成camelCase
camelCase(‘Foo Bar’) ==> ‘fooBar’
function camelCase(string) {
if (typeof string !== "string") return;
let newString = string.split("");
for (let i = 0; i < newString.length; i++) {
let currentString = newString[i];
//遇到空格的時候把下一個值轉為upperCase
if (!currentString.trim().length) {
newString.splice(i, 1)
console.log('newString', newString)
newString[i] = newString[i].toUpperCase()
} else {
newString[i] = currentString.toLowerCase()
}
}
return newString.join("");
}
string轉成snakeCase
snakeCase(‘Foo Bar’) ==> ‘foo_bar’
function snakeCase(string) {
if (typeof string !== 'string') return
let newString = string.toLowerCase().split('')
for (let i=0; i<string.length; i++) {
let tempString = newString[i]
if (!tempString.trim().length) {
newString[i] = '_'
}
}
return newString.join('')
}
unique value in an array
// Example
var arr = [
1,
1,
"true",
"true",
true,
true,
15,
15,
false,
false,
undefined,
undefined,
null,
null,
NaN,
NaN,
"NaN",
0,
0,
"a",
"a",
{},
{},
];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
//using reduce
function unique(arr) {
return arr.reduce(
(prev, cur) => (prev.includes(cur) ? prev : [...prev, cur]),
[]
);
}
//using Es6 Set
function unique(arr) {
return Array.from(new Set(arr));
}
memoize function
function memoizeMap(fn) {
const map = new Map();
return function (arg) {
if (map.has(arg)) {
return map.get(arg);
}
const cachedArg = arg;
const cachedResult = fn(arg);
map.set(cachedArg, cachedResult);
return cachedResult;
};
}
//Example
let testFn = (foo) => foo + 999;
let memoizeMapFn = memoizeMap(testFn);
memoizeMapFn(1); // map對arg 1生成緩存
memoizeMapFn(1); // 取緩存結果
memoizeMapFn(1); // 取緩存結果
memoizeMapFn(2); // map對arg 2生成緩存
memoizeMapFn(2); // 取緩存結果
memoizeMapFn(1); // 取緩存結果
flatten an array
//Example
let array = [[[1, [1.1]], 2, 3], [4, 5]]
flatten(array)
//[1, 1.1, 2, 3, 4, 5]
//reduce, recursive
function flatten(arr) {
return arr.reduce( (prev, curr) => {
return prev.concat(
Array.isArray(curr) ? flatten(curr) : curr
);
}, []);
}
//while loop, spread opeartor
function flatten(arr) {
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
Array轉JSON tree
function arrayToTree(items, baseLevelId) {
const result = [];
const itemMap = {};
for (const item of items) {
itemMap[item.id] = {...item, children: []}
}
for (const item of items) {
const id = item.id;
const pid = item.pid;
const treeItem = itemMap[id];
if (pid === baseLevelId) {
result.push(treeItem);
} else {
if (!itemMap[pid]) {
itemMap[pid] = {
children: [],
}
}
itemMap[pid].children.push(treeItem)
}}
return result;
}
array轉JSON tree完整文章:
實現 instanceof
function _instanceof(L, R) {
if (typeof L !== "object") return false;
L = L.__proto__;
R = R.prototype;
while (true) {
if (L === null) return false;
if (L === R) return true;
L = L.__proto__;
}
}
資源:
https://0529bill.github.io/bywater-blog/Javascript/Basics/commonMethod