# 手写系列
# 手写instanceof
代码如下:
function myInstanceof(left, right) {
let left_prototype = left.__proto__;
let right_prototype = right.prototype;
while (left_prototype !== null) {
if (left_prototype === right_prototype) return true;
left_prototype = left_prototype.__proto__;
}
return false;
}
// 开始测试
let a = [];
let b = {};
function Foo(){};
let c = new Foo();
function child(){};
function father(){};
child.prototype = new father();
let d = new child();
console.log(myInstanceof(a, Array)); // true
console.log(myInstanceof(b, Object)); // true
console.log(myInstanceof(b, Array)); // false
console.log(myInstanceof(a, Object)); // true
console.log(myInstanceof(c, Foo)); // true
console.log(myInstanceof(d, child)); // true
console.log(myInstanceof(d, father)); // true
# 手写call
准备一个add函数:
function add(a, b) {
console.log(this, a, b);
return a + b;
}
第一版:
Function.prototype.myCall = function(obj, ...args) {
obj.fn = this;
obj.fn(...args);
delete obj.fn;
}
add.myCall({}, 1, 2); // {fn: ƒ} 1 2
第二版:
Function.prototype.myCall = function(obj, ...args) {
let key = Symbol('fn');
obj[key] = this;
obj[key](...args);
delete obj[key];
}
add.myCall({}, 1, 2); // {Symbol(fn): ƒ} 1 2
第三版:
Function.prototype.myCall = function(obj, ...args) {
let key = Symbol('fn');
Object.defineProperty(obj, key, {
enumerable: false,
value: this
})
obj[key](...args);
delete obj[key];
}
add.myCall({}, 1, 2); // {} 1 2
最终版:
Function.prototype.myCall = function(obj, ...args) {
obj = (obj === null || obj === undefined) ? globalThis : Object(obj);
let key = Symbol('fn');
Object.defineProperty(obj, key, {
enumerable: false,
value: this
})
let result = obj[key](...args);
delete obj[key];
return result;
}
add.myCall({}, 1, 2); // {} 1 2
console.log(Object.prototype.toString.myCall(1)); // [object Number]
console.log(Object.prototype.toString.myCall([])); // [object Array]
# 手写数组转树
let input = [
{ id: 2, val: '班级1', parentId: 1 },
{ id: 6, val: '学生3', parentId: 3 },
{ id: 4, val: '学生1', parentId: 2 },
{ id: 3, val: '班级2', parentId: 1 },
{ id: 1, val: '学校', parentId: null },
{ id: 5, val: '学生2', parentId: 2 },
]
function arrayToTree(array) {
let root = null, queue = [];
array = array.sort((a, b) => a.parentId - b.parentId);
for (let i = 0, len = array.length; i < len; ) {
let item = array[i];
if (i === 0) {
root = {
id: item.id,
val: item.val,
children: []
};
queue.push({ id: item.id, child: root.children });
i++;
} else {
while (i < len && queue[0].id === array[i].parentId) {
let obj = { id: array[i].id, val: array[i].val, children: [] };
queue[0].child.push(obj);
queue.push({ id: array[i].id, child: obj.children });
i++;
}
queue.shift();
}
}
return root;
}
console.log(arrayToTree(input));
# 防抖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
let input = document.querySelector('input');
function debounce(fn, delay) {
let timer = null;
return function() {
let that = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(that, args);
}, delay);
}
}
function test(a, b) {
console.log("res: ", a + b);
}
input.addEventListener('input', debounce(test, 1000).bind(null, 1, 2)); // window
</script>
</body>
</html>
# 节流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
let input = document.querySelector('input');
function throttle(fn, delay) {
let previousTime = 0;
return function() {
let currentTime = +new Date();
if(currentTime - previousTime > delay) {
fn.apply(this, arguments);
previousTime = currentTime;
}
}
}
function test(e, a, b) {
console.log("res: ", a + b);
console.log(e);
}
input.addEventListener('input', throttle(test, 1000));
</script>
</body>
</html>
# 深拷贝
function deepClone(data) {
const type = Object.prototype.toString.call(data).slice(8, -1);
if (!['Object', 'Array'].includes(type)) return data;
const newData = type === 'Object' ? {} : [];
for (let key in data) {
const temp = data[key];
if (typeof temp === 'object' && temp !== null) {
newData[key] = deepClone(temp);
} else {
newData[key] = temp;
}
}
return newData;
}
let a = [1, 2, { a: 1, b: [1, 2, 3, 4] }, 4, null, undefined, function name() {}],
b = deepClone(a);
a[2].a = 2;
console.log(a);
console.log(b);
# 判断是否为空对象
function isEmptyObject(data) {
return data === null ||
(typeof data === 'object' && !Reflect.ownKeys(data).length);
}
let obj = {};
Object.defineProperty(obj, 'haha', {
enumerable: false,
value: 1
})
console.log(isEmptyObject(null)); // true
console.log(isEmptyObject(undefined)); // false
console.log(isEmptyObject(0)); // false
console.log(isEmptyObject('')); // false
console.log(isEmptyObject({ [Symbol('symbol')]: 'haha' })); // false
console.log(isEmptyObject({ a: 1 })); // false
console.log(isEmptyObject({ })); // true
console.log(isEmptyObject(obj)); // false
console.log(isEmptyObject([])); // false
console.log(isEmptyObject([1, 2, 3])); // false
# 颜色值16进制转10进制rgb
function hexToRGB(str) {
let len = str.length, result = [];
if (!/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(str)) throw new Error('invalid color hex');
let step = (len - 1) / 3, num = step === 1 ? 2 : 1;
for (let i = 1; i < len; i+=step) {
result.push(Number.parseInt(str.slice(i, i + step).repeat(num), 16));
}
return `rgb(${result.join(",")})`;
}
console.log(hexToRGB('#f0ffff'));
console.log(hexToRGB('#f0f'));
console.log(hexToRGB('#f0ff'));
# 数组扁平化
使用内置函数:
let arr = [1, [2, [3, 4]]];
console.log(arr.flat(Infinity));
递归1:
function flatArray(array) {
const result = [];
recursion(array)
return result;
function recursion(array) {
for (let item of array) {
Array.isArray(item) ? recursion(item) : result.push(item);
}
}
}
console.log(flatArray([1, 2, [3, 4, [5, 6, [7, 8, 9], 10], 11, 12], 13, 14, 15]));
递归2:
function flatArray(array) {
let result = [];
for (let item of array) {
Array.isArray(item) ?
(result = [...result, ...flatArray(item)]) : result.push(item);
}
return result;
}
console.log(flatArray([1, 2, [3, 4, [5, 6, [7, 8, 9], 10], 11, 12], 13, 14, 15]));
# 生成长度是N,且在min、max内不重复的 整数随机数组
function rand(min, max, N) {
N = max - min + 1 > N ? N : max - min + 1;
let set = new Set();
while (set.size < N) {
set.add(Math.round(Math.random() * (max - min) + min));
}
return [...set];
}
console.log(rand(0, 5, 3));
# 字符串中的单词逆序输出
方法1:
function strReverse(str) {
return str.split("").reverse().join("");
}
方法2:
function strReverse(str) {
let stack = [], result = "";
for (let char of str) stack.push(char);
while (stack.length) result += stack.pop();
return result;
}
# 分片渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box"></div>
<script>
let patch = 20, nowNum = 0, cnt = 0, timer = null;
const time = 200, allNum = 1000000, box = document.querySelector("#box");
// 直接渲染
let frag = document.createDocumentFragment();
for (let i = 0; i < allNum; i++) {
let div = document.createElement('div');
div.innerText = `这是第 ${i} 个内容`;
frag.appendChild(div);
}
box.appendChild(frag);
// 分片渲染
timer = setInterval(() => {
let frag = document.createDocumentFragment();
for (let i = 0; i < patch; i++) {
let div = document.createElement('div');
div.innerText = `这是第 ${++cnt} 个内容`;
frag.appendChild(div);
}
box.appendChild(frag);
nowNum += patch;
if (nowNum === allNum) clearInterval(timer);
}, time);
</script>
</body>
</html>
# 虚拟列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 300px;
border: 1px solid black;
overflow: scroll;
position: relative;
}
.empty,
.container {
width: 100%;
}
.container {
position: absolute;
left: 0;
}
.empty {
position: absolute;
top: 0;
left: 0;
}
.container span {
width: 100%;
display: inline-block;
text-align: center;
}
</style>
</head>
<body>
<div id="box">
<div class="empty"></div>
<div class="container"></div>
</div>
<script>
const allNum = 100000, itemHeight = 30, screenHeight = 300, maxHeight = allNum * itemHeight - screenHeight;
const box = document.querySelector("#box"),
empty = box.querySelector(".empty"),
container = box.querySelector(".container");
let start = 0, end = Math.ceil(screenHeight / itemHeight);
box.style.height = `${screenHeight}px`;
empty.style.height = `${itemHeight * allNum}px`;
updateData();
// 更新数据
function updateData() {
let frag = document.createDocumentFragment();
for (let i = start;i <= allNum && i <= end; i++) {
let span = document.createElement('span');
span.style.height = `${itemHeight}px`;
span.innerText = `这是第 ${i} 个数据`;
frag.appendChild(span);
}
container.innerHTML = ``;
container.appendChild(frag);
}
box.addEventListener('scroll', function (e) {
let top = this.scrollTop;
start = Math.floor(top / itemHeight);
end = Math.floor((top + screenHeight) / itemHeight);
updateData();
container.style.transform = `translateY(${top}px)`;
})
</script>
</body>
</html>
# 实现Array.prototype.at方法
function rewriteArray(array) {
if (!Array.isArray(array)) {
throw new Error('The parameter passed in is not an array!');
}
return new Proxy(array, {
get(target, property) {
if (property.startsWith("-")) {
if (/^-[1-9]\d{0,}$/.test(property)) {
return target[target.length + +property];
}
throw new Error('invalid index!');
} else {
return target[property];
}
}
})
}
let list = rewriteArray([1, 2, 3, 4, 5]);
console.log(list[-2]); // 4