注:以下内容为随手笔记,属于暂未成形的文章,请勿参考
「手写代码」
模拟实现深拷贝
核心:
浅拷贝复制引用的拷贝方法,深拷贝就是指完全的拷贝一个对象。这里说的拷贝仅针对数组和对象。
function shallowCopy(obj) {
// 基础类型返回
if (typeof obj !== 'object' || obj === null) return obj;
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
function deepCopy(obj) {
// 基础类型返回
if (typeof obj !== 'object' || obj === null) return obj;
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
实现节流和防抖
防抖
函数在被触发的n妙后执行,在n秒内又触发,则重新计数
- search搜索,用户不断输入值时,用防抖来节约Ajax请求,也就是输入框事件
- window触发resize时,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
function debounce(fn, delay, isImmediate) {
let timer = null;
return function () {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
if (isImmediate) {
if (!timer) fn.apply(context, args)
timer = setTimeout(function () {
timer = null;
}, delay);
} else {
timer = setTimeout(function () {
fn.apply(context, args)
}, delay);
}
}
}
节流
一段时间内,只执行函数一次。如果这段时间内多次触发,也只执行一次函数
- 鼠标的点击事件mousedown只触发一次
function throttle(fn, delay) {
let base = 0;
let timer = null;
return function() {
let context = this;
let args = arguments;
let now = +new Date();
let remain = delay - (now - base);
if(remain <= 0 || remain > delay) {
if(timer) {
clearTimeout(timer);
timer = null;
}
base = now;
fn.apply(context, args);
} else if(!timer) {
timer = setTimeout(() => {
base = +new Date();
timer = null;
fn.apply(context, args);
}, remain)
}
}
}
实现函数柯里化
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数
const curry = (fn, ...args) =>
args.length < fn.length ? (...arguments) => curry(fn, ...args, ...arguments) : fn(...args);
// 测试实例
function sumFn(a, b, c) {
return a + b + c;
}
var sum = curry(sumFn);
console.log(sun(1)(2)(3));
console.log(sun(1)(2,3));
应用场景:参数复用。本质上是降低通用性,提高适用性。
$.ajax、 $.get、$.post
实现数组去重
function uniq(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
[...new Set(arr)];
实现数组扁平化
function flat2(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
newArr = newArr.concat(flat2(arr[i]));
} else {
newArr.push(arr[i]);
}
}
return newArr;
}
实现数组乱序
核心:
Fisher–Yates:遍历数组元素,然后将当前元素与以后随机位置的元素进行交换
// abnormalSort
function shuffle(arr) {
for (let i = arr.length; i > 0; i--) {
let j = Math.floor(Math.random() * i);
[arr[i - 1], arr[j]] = [arr[j], arr[i - 1]];
}
return arr;
}
其他
实现一个observer递归绑定所有属性
function observer2(obj) {
return bindProperty(obj);
}
function bindProperty(obj) {
if(typeof obj !== 'object' || obj === null) {
return
}
Object.keys(obj).forEach(key => {
let value = obj[key];
if(typeof value !== 'object') {
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
console.log('set old value', value);
console.log('set new value', newVal);
}
})
} else {
bindProperty(obj[key])
}
});
return obj;
}
let test = observer2({ b: 1, c: 3, d: { e: 3 } });
test.b = 2;
模拟实现Promise
function myPromise(construtor) {
let self = this;
self.status = 'pending';
self.value = undefined;
self.error = undefined;
// 初始化后立即执行
try {
construtor(resolve, rejected);
} catch(e) {
rejected(e);
}
function resolve(value) {
if(self.status === 'pending') {
self.status = 'resolved';
self.value = value;
}
}
function rejected(error) {
if(self.status === 'pending') {
self.status = 'rejected';
self.error = error;
}
}
}
myPromise.prototype.then = (A, B) => {
let self = this;
if(self.status === 'resolved') {
A(self.value);
}
if(self.status === 'rejected') {
B(self.error);
}
}
实现兼容IE的事件监听
// 兼容浏览器
function bindEvent(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, eventFn);
} else {
obj.attachEvent("on" + type, eventFn);
}
function eventFn(e) {
const e = e || window.event;
const target = e.target || e.srcElemet;
fn && fn(target, e);
}
}
手写XHR实现
const req = new XMLHttpRequest();
req.open('GEt', '/', true);
req.onreadystatechange = () => {
if(req.readyState === 4 && req.status === 200) {
console.log(res)
}
}
req.send();
实现事件委托
function trust(element, type, fn) {
element.addEventListener(type, e => {
const current = e.target;
if(element.contains(current)) {
fn.call(current, e)
}
});
return element;
}
实现trim函数
var str = ' 324 ';
String.prototype.trim2 = function() {
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
str.trim2();
实现大数相加
function addStrs(str1, str2) {
let i = str1.length - 1, j = str2.length - 1, add = 0;
const arr = [];
while( i >= 0 || j >= 0 || add !== 0) {
const x = parseInt(str1.charAt(i));
const y = parseInt(str1.charAt(i));
const res = x + y + add;
arr.push(res % 10);
add = Math.floor(res / 10);
i--, j--;
}
return arr.reverse().join('');
}
// 测试
var str1 = '13265456465', str2 = '26521132';
addStrs(str1, str2);
实现拖拽
// position: relative;
let dragging = false
let position = null
const xxx = document.querySelector('#xxx');
xxx.addEventListener('mousedown', function(e) {
dragging = true
position = [e.clientX, e.clientY]
});
document.addEventListener('mousemove', function(e) {
if(dragging === false) return null
const x = e.clientX
const y = e.clientY
const deltaX = x - position[0]
const deltaY = y - position[1]
const left = parseInt(xxx.style.left || 0)
const top = parseInt(xxx.style.top || 0)
xxx.style.left = left + deltaX + 'px'
xxx.style.top = top + deltaY + 'px'
position = [x, y]
});
document.addEventListener('mouseup', function(e){
dragging = false
});
实现Event类,on off emit
class Event {
constructor () {
// 储存事件的数据结构
// 为查找迅速, 使用对象(字典)
this._cache = {}
}
// 绑定
on(type, callback) {
// 为了按类查找方便和节省空间
// 将同一类型事件放到一个数组中
// 这里的数组是队列, 遵循先进先出
// 即新绑定的事件先触发
let fns = (this._cache[type] = this._cache[type] || [])
if(fns.indexOf(callback) === -1) {
fns.push(callback)
}
return this
}
// 触发
// emit
trigger(type, data) {
let fns = this._cache[type]
if(Array.isArray(fns)) {
fns.forEach((fn) => {
fn(data)
})
}
return this
}
// 解绑
off (type, callback) {
let fns = this._cache[type]
if(Array.isArray(fns)) {
if(callback) {
let index = fns.indexOf(callback)
if(index !== -1) {
fns.splice(index, 1)
}
} else {
// 全部清空
fns.length = 0
}
}
return this
}
}
实现getValue
function deepGet(obj, keys, defaultVal) {
return (
(!Array.isArray(keys)
? keys.replace(/\[/g, '.').replace(/\]/g, '').split('.')
: keys
).reduce((o, k) => (o || {})[k], obj) || defaultVal
);
}
// 测试实例
var obj = {
a: [
{
b: {
c: 3,
},
},
],
e: {
f: 1,
},
};
console.log(deepGet(obj, 'e.f')); // 1
console.log(deepGet(obj, ['e','f'])) // 1
console.log(deepGet(obj, 'a.x')); // undefined
console.log(deepGet(obj, 'a.x', '--')) // --
console.log(deepGet(obj, 'a[0].b.c')) // 3
console.log(deepGet(obj, ['a', 0, 'b', ,'c'])) // 3
参考
「数据结构」
数组和链表
栈和队列
树
普通二叉树、平衡二叉树、完全二叉树、二叉搜索树
二叉树的便利
「算法」
排序算法
冒泡
时间复杂度O(n*n)
- 比较相邻的元素,前者比后者大的话,两者交换位置。循环操作
- 除去最后一个元素,剩下的重复上述步骤
function bubbleSort(arr) {
const len = arr.length;
for(let i = 0; i < len - 1; i++) {
for(let j = 0; j < len - 1 -i; j++) {
if(arr[j] > arr[j+1]) {
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr;
}
快速
时间复杂度:O(nlogn)
- 取中间那个数做基数,准备两个数组容器,剩下元素逐个与基数比对,较小的放左边容器,较大的放右边容器
- 递归处理两个容器的元素,并将处理后的数据与基数按大小合并成一个数组
function quickSort(arr) {
let len = arr.length;
if(len <= 1) {
return arr;
}
let index = Math.floor(len / 2);
let middle = arr.splice(index, 1);
let left = [], right = [];
for(let i = 0; i < arr.length; i++) {
if(arr[i] < middle[0]) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat(middle, quickSort(right));
}
插入
时间复杂度: O(n*n)
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
function insertSort(arr) {
for(let i = 0; i < arr.length; i++) {
let preIndex = i - 1,
cur = arr[i];
while(preIndex >= 0 && arr[preIndex] > cur) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = cur;
}
return arr;
}
选择
时间复杂度O(n*n)
每一次从待排序的数组元素中选择最大(最小)的一个元素作为首元素,直到排完为止
function selectSort(arr) {
let len = arr.length, temp = 0;
for(let i = 0; i < len; i++) {
temp = i;
for(let j = i + 1; j < len; j++) {
if(arr[j] < arr[temp]) {
temp = j;
}
}
if(temp !== i) {
[arr[i], arr[temp]] = [arr[temp], arr[i]];
}
return arr;
}
}
动态规划
搜索算法
深度优先搜索算法
(简称为 DFS)
广度优先搜索算法
(简称为 BFS)