手写代码
冒泡排序
时间复杂度:O(n^2),空间复杂度:O(1)
// 两个相邻的数比较,较大的往后挪,则每一趟都能找到最大的数
function bubbleSort(arr){
// 趟数
for (let i = 0; i < arr.length - 1; i ++) {
// 次数
for (let j = 0; j < arr.length - i - 1; j ++) {
if(arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 优化,加个标志位,如果在某一轮发现没有交换数据,那就说明排序已经完成了,可以 break
function bubbleSort1(arr){
let flag = true;
for (let i = 0; i < arr.length - 1; i ++) {
for (let j = 0; j < arr.length - 1 - i; j ++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
}
if (flag) {
break;
}
}
}
选择排序
时间复杂度:O(n^2)
// 初始以第一个为最小值,然后与后面的每一个挨个比较,更小的交换到最小值的 index 上
function selectSort(arr){
var minIndex = 0;
for (let i = 0; i < arr.length; i ++){
// 总是将未排序的第一个数字当作最小值的比对标准
minIndex = i;
for (let j = i + 1; j < arr.length; j ++) {
if (arr[j] < arr[minIndex]) {
// 找到比最小值更小的值,将索引换成更小值的索引
minIndex = j;
}
}
// 交换未排序的第一个值和最小值的位置
var temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
快排
function quickSort(arr){
let pivotIndex = Math.floor(arr.length / 2);
let pivotValue = arr[pivotIndex];
let left = [];
let right = [];
for (let i = 0; i < arr.length; i ++) {
if (arr[i] < pivotValue) {
left.push(arr[i]);
}
if (arr[i] > pivotValue) {
right.push(arr[i]);
}
}
return quickSort(left).concat(pivotValue).concat(quickSort(right));
}
二分查找
function erfen(arr, value){
let start = 0;
let end = arr.length - 1;
while(start < end){
let mid = Math.floor((start + end) / 2);
if (value === arr[mid]) {
return mid;
}
if (value < arr[mid]) {
end = mid - 1;
}
if (value > arr[mid]) {
start = mid + 1;
}
}
}
深度遍历
// 递归
function deep(node, result){
if (node) {
result.push(node);
const children = node.children;
if (children) {
for (let i = 0; i < children.length; i ++) {
deep(children[i], result);
}
}
}
return result;
}
// 栈
function deep2(node){
let result = [];
let stack = [];
stack.push(node);
while(stack.length){
const currentNode = stack.pop();
let children = currentNode.children;
result.push(currentNode);
for (let i = children.length - 1; i >= 0; i --){
stack.push(children[i]);
}
}
return result;
}
广度遍历
// 队列
function queue2(node) {
let result = [];
let queue = [];
result.push(node);
while(queue.length){
const currentNode = queue.shift();
const children = currentNode.children;
result.push(currentNode);
for (let i = 0; i < children.length; i ++) {
queue.push(children[i]);
}
}
return result;
}
前序遍历二叉树
// 递归
function q1(treeNode){
if (treeNode) {
console.log(treeNode.value);
treeNode.left && q1(treeNode.left);
treeNode.right && q1(treeNode.right);
}
}
// 栈
function q2(treeNode){
let stack = [];
stack.push(treeNode);
while(stack.length){
const currentNode = stack.pop();
currentNode.right && stack.push(currentNode.right);
currentNode.left && stack.push(currentNode.left);
console.log(currentNode);
}
}
中序遍历二叉树
// 递归
function z1(treeNode){
if (treeNode) {
z1(treeNode.left);
console.log(treeNode.value);
z2(treeNode.right);
}
}
// 栈
// 把当前节点入栈,然后遍历左子树,如果左子树存在就一直入栈,直到左子树为空,访问但前节点,然后让右子树入栈
function z2(treeNode){
let stack = [];
while(treeNode || stack.length){
if (node) {
stack.push(node);
node = node.left;
} else {
node = stack.pop();
console.log(node.value);
node = node.right;
}
}
}
后序遍历二叉树
// 递归
function h1(treeNode){
if (treeNode) {
h1(treeNode.right);
h2(treeNode.left);
console.log(treeNode.value);
}
}
// 栈
// 先把根结点和左树推入栈,然后取出左树,再推入右树,取出,最后取根结点。
function h2(treeNode){
if(treeNode){
let stack = [];
let topNode = null;
stack.push(treeNode);
while(stack.length){
topNode = stack[stack.length - 1];
if (topNode.left) {
stack.push(topNode.left);
}
if (topNode.right){
stack.push(topNpde.right);
}
console.log(stack.pop().value);
treeNode = topNode;
}
}
}
最长无重复子串
function d(str){
let end = 0;
let visit = new Map();
let result = "";
for (let i = 0; i < str.length; i ++){
if (i > 0){
visit.delete(str[i - 1]);
}
while(end < str.length && !visit.get(end + 1)){
visit.set(str[end + 1], true);
end ++;
}
result = Math.max(result, end - i + 1);
}
return result;
}
观察者模式
- 只需要两个角色:观察者和被观察者,重点是被观察者的实现
// 被观察者
class Subject {
constructor(){
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removerObserver(observer) {
this.observers = this.observers.filter((o) => o.name === observer.name);
}
notifyObserver(message) {
this.observers.forEach((observer) => {
observer.notified(message);
});
}
}
// 观察者
class Observer {
constructor(name) {
this.name = name;
}
notified(message){
console.log(this.name, message);
}
}
// 使用
const subject = new Subject();
const observerA = new Observer("observerA");
const observerB = new Observer("observerB");
subject.addObserver(observerA);
subject.addObserver(observerB);
subject.notified("hello");
发布订阅模式
- 需要三个角色:发布者、订阅者、发布订阅中心,重点是发布订阅中心的实现
// 发布订阅中心
class PubSub {
constructor(){
this.listners = {};
this.message = {};
}
// 添加订阅者
subscribe(type, cb){
if (!this.listners[type]) {
this.listners[type] = [];
}
this.listners[type].push(cb);
}
// 发布消息
publish(type, content){
if (!this.message[type]){
this.message[type] = [];
}
this.message[type].push(content);
}
notify(type){
const message = this.message[type];
const subscriber = this.listners[type];
subscriber.forEach((sub, index) => {
sub(message[index]);
});
}
}
// 订阅者
class Subscriber {
constructor(name, subPub){
this.name = name;
this.subPub = subPub;
}
subscribe(type, cb){
this.subPub.subscribe(type, cb);
}
}
// 发布者
class Publisher {
constructor(name, subPub){
this.name = name;
this.subPub = subPub;
}
publish(type, message){
this.subPub.publish(type, message);
}
}
const subPub = new SubPub();
const publisherA = new Publisher("publisherA", subPub);
const subscriberA = new Subscriber("subscriberA", subPub);
publisherA.publish("typeA", "AAAAA");
subscriberA.subscribe("typeA", (msg) => {
console.log("typeA 发布的消息:", msg);
});
封装一个有时效性的 localStorage
// 和数据一起存一个过期时间,每次取值都判断一下是否过期
class timeEfficientLocalStorage {
set(key, value, expires){
const times = Date.now() + new Date(expires).getTime();
localStorage.setItem(key, JSON.stringify({
value: value,
expires: times
}));
}
get(key){
const content = JSON.parse(localStorage.getItem(key));
if (Date.now() > content.expires){
localStorage.removeItem(key);
return false;
} else {
return content.value;
}
}
}
实现 promise
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function MyPromise(executor){
this.status = PENDING;
this.value;
this.reason;
this.fulfilledCbs = [];
this.rejectedCbs = [];
function resolve(value){
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
function reject(reason){
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
try{
excutor(resolve, reject);
}catch(e){
rejected(e)
}
}
MyPromise.prototype.then = function(resolveCb, rejectCb){
return new MyPromise(resolve, reject){
if (this.status === FULFILLED) {
resolve(this.value);
}
if (this.status === REJECTED) {
reject(this.reason);
}
if (this.status === PENDING) {
this.fulfilledCbs.push(function(){
resolve(resolveCb(this.value));
});
this.rejectedCbs.push(function(){
reject(rejectCb(this.reason));
});
}
}
}
设计 Promise.all
Promise.all = (promises) => {
let result = [];
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
promise.then((res) => {
if (count === promises.length) {
resolve(result);
} else {
result.push(res);
}
count ++;
}).catch((err) => {
reject(err);
});
});
});
}
sum(2)(3)
function sum(){
function add(m, n){
return m + n;
}
const args = [...arguments];
return function(){
const _args = [...arguments];
return add.apply(this, [...args, ..._args]);
}
}
// 柯里化包装函数
function curry(fn){
const result = () => {
return [...arguments].length === fn.length
? fn(...arguments)
: (..._args) => { result(...args, ..._args) }
}
return result;
}
比较两个对象
// 1.引用相等性(使用 `===`、 `==` 或 `Object.is()`)用来确定操作数是否为同一个对象实例
// 2.手动遍历比较每个属性
function isObject(v){
return v !== null && typeof v === "object";
}
function deepEqual(obj1, obj2){
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (let i = 0; i < keys1.length; i ++) {
const v1 = obj1[keys1[i]];
const v2 = obj2[keys2[i]];
if (isObject(v1) && isObject(v2)) {
deepEqual(v1, v2);
}
if (v1 !== v2){
return false;
}
}
return true;
}
防抖
function debounce(fn, interval){
let timer = null;
return function() {
if (timer) {
clearTimeout(timer);
} else {
timer = setTimeout(() => {
fn.call(this, arguments);
}, interval);
}
}
}
节流
function throttle(fn, interval){
let canRun = true;
return () => {
if (!canRun) {
return;
}
canRun = false;
setTimeour(() => {
canRun = true;
fn(arguments);
}, interval);
}
}
并发控制
class LimitRequest {
constructor(allRequests, maxNum){
this.allRequests = allRequests;
this.maxNum = maxNum;
this.count = 0;
}
run(){
if (this.count < this.maxNum) {
const p = this.allRequests.shift();
p().then((res) => {
console.log(res);
}).finally(() => {
this.count ++;
this.run();
});
} else if (this.allRequests.length) {
this.count = 0;
this.run()
}
}
}
useState & useEffect
let states = [];
let setters = [];
let cursor = 0;
let firstRun = true;
function useState(value) {
if (firstRun) {
states.push(value);
const setState = (newValue) => {
state[cursor] = newValue;
}
setters.push(setState);
firstRun = false;
}
cursor ++;
return [states[cursor], setters[cursor]];
}
let depsList = [];
let index = 0;
let clearCbs = [];
function useEffect(cb, deps){
const lastDeps = depsList[index];
const canRun = !deps || !depsList[depIndex] || depsList.some((item, index) => item !== lastDeps[index]);
if (canRun) {
depsList[index] = deps;
if (clearCbs[index]) {
clearCbs[index]();
}
clearCbs[index](cb());
}
depIndex ++;
};
深拷贝
function deepClone(obj, map = new WeakMap()){
// 防止循环引用
if (map.get(obj)) {
return map.get(obj);
}
let result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === null || typeof obj[key] !== "object") {
result[key] = obj[key];
} else if (Array.isArray(obj[key])) {
result[key] = obj[key].map(item => deepClone(item, map));
} else if (obj[key] instanceof Set) {
result[key] = new Set([...obj[key]]);
} else if (Object.prototype.toString.call(obj[key] === "object Date")) {
result[key] = new Date(obj[key]);
} else if (Object.prototype.toString() === "object Object") {
map.set(obj[key], obj[key]);
result[key] = deepClone(obj[key], map);
} else {
result[key] = obj[key];
}
}
}
return result;
}
实现 call、apply、bind
Function.prototype.myCall = function(obj){
obj = obj ? Object(obj) : window;
obj.fn = this;
const result = obj.fn(Array.prototype.slice(arguments, 1));
return result;
}
Function.prototype.myApply = function(obj, argsArr){
const obj = obj ? Object(obj) : window;
obj.fn = this;
const result = obj.fn(...argsArr);
return result;
}
Function.prototype.bind = function(obj){
const args = Array.prototype.slice(arguments, 1);
const that = this;
return function(){
const _args = Array.prototype.slice(arguments);
that.apply(obj, [...args, ..._args]);
}
}
getUrlParam(url, key)
function getUrlParam(url, key){
if (!url){
return null;
}
const searchStr = window.location.search.slice(1);
const searchStrToArr = searchStr.split("&");
let result = {};
searchStrToArr.forEach((item) => {
const itemArr = item.split("=");
result[itemArr[0]] = itemArr[1];
});
return result[key];
};
数组扁平化
function flat(arr){
const result = arr.reduce((pre, curr) => {
return pre.concat(Array.isArray(curr) ? flat(curr) : curr);
}, []);
return result;
}
柯里化
function curry(fn){
const resultFn = (...args) => {
if (args.length === fn.length) {
return fn(...args);
} else {
return (..._args) => {
resultFn(...args, ..._args);
};
}
}
return resultFn;
}
sleep 函数
function sleep(time){
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
instanceof
function instanceof(left, right){
let leftProto = left.__proto__;
const rightPrototype = right.prototype;
while(leftProto){
if (leftProto === rightPrototype) {
return true;
}
leftProto = leftProto.__proto__;
}
if (leftProto === null) {
return false;
}
}
斐波那契数列
function f(n, total=1){
if (n === 1) {
return total;
}
return f(n-1, n * total);
}
打平数组
let result = [];
function flatten(arr){
for (let i = 0; i < arr.length; i ++){
if (Array.isArray(arr[i])){
flatten(arr[i]);
} else {
result.push(arr[i]);
}
}
}
// 2
function flatten2(arr){
return arr.reduce((pre, curr) => Array.isArray(curr) ? flatten2(curr) : pre.concat(curr), []);
}
简单封装 fetch
function request(url, config){
let reqUrl = url;
const initial = {
method: "GET",
params: null,
body: null,
headers: {
"Content-Type": "application/x-www-form-urlencode"
},
credentials: true,
cache: "no-cache"
}
if (config.headers) {
config.headers = Object.assign({}, initial.headers, config.headers);
}
let {
method,
params,
body,
credentials,
headers
} = Object.assign({}, initial, config);
if (params !== null) {
reqUrl = `${reqUrl}?${qs.stringify(params)}`;
}
if (body !== null) {
let contentType = headers["Content-Type"] || "application/json";
if (contentType.includes("urlencoded")) {
body = qs.stringify(body);
}
if (contentType.includes("json")) {
body = JSON.stringify(body);
}
}
credentials = credentials ? "include" : "same-origin";
method = method.toUpperCase();
const controller = new AbortController();
let resultConfig = {method,credentials, headers, body};
fetch(reqUrl, {...resultConfig, signal: controller.signal}).then((res) => {
let {status, statusText} = res;
if (status >= 200 && status < 300) {
res.json()
return res.json();
}
return Promise.reject({
status,
code: "error",
statusText
});
}).catch((error) => {
if (error.code === "error") {
return Promise.reject(error);
}
});
}
function get(url, params){
return request(url, {
methohd: "get",
params,
});
}
function post(url, params){
return request(url, {
headers: {
'content-type': 'application/json',
},
method: 'POST',
body: JSON.stringify(params)
});
}
判断对象中是否有循环引用
function isCircle(obj){
let map = new Map();
let result = false;
function fn(obj){
if (typeof obj !== "object") {
return;
}
if (map.get(obj)) {
result = true;
}
map.add(obj, obj);
for (let key in obj) {
if (obj.hasOwnProperty(obj[key])) {
fn(obj[key]);
}
}
map.delete(obj);
}
fn(obj);
return result;
}