找出字符串中最多的字符
function strMaxNum(str) {
let obj = {};
for (let i of str) {
obj[i] = obj[i] ? (obj[i] += 1) : 1;
}
let count = 0;
let keyName = "";
for (let key in obj) {
if (obj[key] > count) {
count = obj[key];
keyName = key;
}
}
console.log("次数最多字符" + keyName + ":", count);
}
strMaxNum("12342223");
获取url地址中的参数
function getUrl(url){
let str=url.split('?')
if(str[0]==url){
return ''
}
let obj={}
let arr=str[1].split('&')
for(let i=0;i<arr.length;i++){
let sd=arr[i].split('=')
obj[sd[0]]=sd[1]
}
return obj
}
getUrl('http://www.baidu.com/s?name=sd&id=23232')
{name: 'sd', id: '23232'}
数组去重的六种方式
1. new Set()
1. function noRepeat(arr){
1. var newArr = [...new Set(arr)]; //利用了Set结构不能接收重复数据的特点
1. return newArr
1. }
1.
2. filter()
var arr = ['apple','apps','pear','apple','orange','apps'];
console.log(arr)
var newArr = arr.filter(function(item,index){
return arr.indexOf(item) === index; // 因为indexOf 只能查找到第一个
});
console.log(newArr);
3. for+ indexOf
var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
function noRepeat(arr) {
//定义一个新的临时数组
var newArr=[];
//遍历当前数组
for(var i=0;i<arr.length;i++) {
//如果当前数组的第i已经保存进了临时数组,那么跳过,
//否则把当前项push到临时数组里面
if(newArr.indexOf(arr[i]) === -1) { //indexOf() 判断数组中有没有字符串值,如果没有则返回 -1
newArr.push(arr[i]);
}
}
return newArr
}
var arr2 = noRepeat(arr);
console.log(arr2);
4. 双重for循环
var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
console.log(arr);
function noRepeat(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length; j++) {
if (arr[i] == arr[j] && i != j) { //将后面重复的数删掉
arr.splice(j, 1);
}
}
}
return arr;
}
var arr2 = noRepeat(arr);
console.log(arr2);
5. for+includes
var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
function noRepeat(arr) {
let newArr = [];
for(i=0; i<arr.length; i++){
if(!newArr.includes(arr[i])){
newArr.push(arr[i])
}
}
return newArr
}
console.log(noRepeat(arr));
深拷贝手写
function deepClone(obj){
let objClone=Array.isArray(obj)?[]:{};
if(obj&&typeof obj=='object'&&obj!==null){
for(let key in obj){
if(obj[key]&&typeof obj[key]=='object'&&obj[key]!==null){
objClone[key]=deepClone(obj[key])
}else{
objClone[key]=obj[key]
}
}
}
return objClone
}
let obj={a:1,b:'ss',c:false,d:null,e:undefined,f:{as:'23'},g:['23',23]}
let test=deepClone(obj)
函数柯里化
柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
核心思想:是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。
function currying(fn, ...rest1) {
return function(...rest2) {
return fn.apply(null, rest1.concat(rest2))
}
}
实现add(1)(2)(3)
function add(...args){
return args.reduce(
(a,b)=> a+b
)
}
function currying(fn){
let args=[]
return function temp(...newArgs){
if(newArgs.length){
args = [...args, newArgs]
return temp
} else {
let val = fn.apply(this, args)
args = []
return val
}
}
}
let addCurry = currying(add)
// console.log(addCurry(1)(2)(3)(4, 5)())
// console.log(addCurry(1)(2)(3, 4, 5)())
// console.log(addCurry(1)(2, 3, 4, 5)())
// console.log(add(1)(2)(3).toString())
防抖
在一段时间内,事件只会最后触发一次。
function debounce(fn,delay=500){
let timer=null
return function(){
if(timer){
return clearTimeout(timer)
}
timer=setTimeout(()=>{
fn()
timer=null
},delay)
}
}
节流
事件,按照一段时间的间隔来进行触发。
function throttle(fn,delay=500){
let timer=null
return function(){
if(timer) return;
timer=setTimeout(()=>{
fn.apply(this.arguments)
timer=null
},delay)
}
}
数组扁平化
将一个多维数组转换为一个一维数组 [1,[2,[3,4,5]]] ==> [1,2,3,4,5]
1、递归实现
let arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
2、reduce 函数迭代
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.reduce(function(prev, next){
return prev.concat(Array.isArray(next) ? flatten(next) : next)
}, [])
}
console.log(flatten(arr));// [1, 2, 3, 4,5]
3、split 和 toString
var arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {
arr = arr.toString().split(',');
var newArr = arr.map((item) => {
item = +item
return item
})
return newArr
}
console.log(flatten(arr)); // [1, 2, 3, 4, 5]
4、通过扩展运算符实现
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
5、ES6 中的 flat
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.flat(Infinity);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
6、使用正则和 JSON 方法
let arr = [1, [2, [3, [4, 5]]], 6];
function flatten(arr) {
let str = JSON.stringify(arr);
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
return JSON.parse(str);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
对象扁平化
function objectFlat(obj = {}) {
const res = {}
function flat(item, preKey = '') {
Object.entries(item).forEach(([key, val]) => {
const newKey = preKey ? `${preKey}.${key}` : key
if (val && typeof val === 'object') {
flat(val, newKey)
} else {
res[newKey] = val
}
})
}
flat(obj)
return res
}
// 测试
const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));
冒泡排序
function bubbleSort(arr){
let lens=arr.length
for(let i=0;i<lens;i++){
for(let j=0;j<lens-j-i;j++){
if(arr[j]>arr[j+1]){
[arr[j],arr[j+1]]=[arr[j+1],arr[j]]
}
}
}
return arr
}
bubbleSort([1,3,6,2,7])
(5) [1, 2, 3, 6, 7]
有一组版本号为:[“1.45.0”, “1.5”, “6”, “2.3.4.5”],将其排序后得到:[ ‘1.5’, ‘1.45.0’, ‘2.3.4.5’, ‘6’ ]
function verSort(arr){
return arr.sort((a,b)=>{
let i=0;
const arr1=a.split('.')
const arr2=b.split('.')
while(true) {
const s1=arr1[i]
const s2=arr2[i]
i++
if(s1===undefined||s2===undefined){
return arr1.length-arr2.length
}
if(s1===s2) continue;
return s1-s2
}
})
}
verSort(["1.45.0", "1.5", "6", "2.3.4.5"])
(4) ['1.5', '1.45.0', '2.3.4.5', '6']
两数求和
const twoSum = function(nums, target) {
// 这里我用对象来模拟 map 的能力
const diffs = {}
// 缓存数组长度
const len = nums.length
// 遍历数组
for(let i=0;i<len;i++) {
// 判断当前值对应的 target 差值是否存在(是否已遍历过)
if(diffs[target-nums[i]]!==undefined) {
// 若有对应差值,那么答案get!
return [diffs[target - nums[i]], i]
}
// 若没有对应差值,则记录当前值
diffs[nums[i]]=i
}
};
图片懒加载
// 获取所有图片的标签
const imgs = document.getElementsByTagName('img')
// 获取可视区域的高度
const viewHeight = window.innerHeight || document.documentElement.clientHeight
// num 计算当前显示到哪一张图片,避免每次都从第一张图片开始检查是否需要
let num = 0, len = imgs.length
function lazyLoad() {
for(let i = num; i < len; i ++) {
// 用可视区域的高度减去匀速顶部距离可视区域的高度
let distance = viewHeight - migs[i].getBoundingClientRect().top
if(distance > 20){
imgs[i].src = imgs[i].getAttribute('data-src')
}
}
}
// 最好添加节流
window.addEventListener('sroll', lazyLoad)
数组与树互转
数组->树
const arrtree = [
{id:1, parentId: null, name: 'a'},
{id:2, parentId: null, name: 'b'},
{id:6, parentId: 3, name: 'f'},
{id:7, parentId: 4, name: 'g'},
{id:8, parentId: 7, name: 'h'},
{id:3, parentId: 1, name: 'c'},
{id:4, parentId: 2, name: 'd'},
{id:5, parentId: 1, name: 'e'}
]
function arrayTree(arr){
if(!Array.isArray(arr) || !arr.length) return
let map = {}
arr.forEach(v => map[v.id] = v)
let list = []
arr.forEach(v => {
const item = map[v.parentId]
if(item){
( item.children || (item.children = [])).push(v)
} else {
list.push(v)
}
})
return list
}
// console.log(arrayTree(arrtree))
树->数组
// 将树数据转化为平铺数据
flatTreeData(treeData: any[], childKey = 'children') {
const arr: any[] = [];
const expanded = (data: any) => {
if (data && data.length > 0) {
data
.filter((d: any) => d)
.forEach((e: any) => {
arr.push(e);
expanded(e[childKey] || []);
});
}
};
expanded(treeData);
return arr;
}
手写 new
const customNew = (Func, ...args) => {
// 创建一个空的简单JavaScript对象
const obj = Object.create({})
// 链接该对象到prototype
obj.__proto__ = Func.prototype
// 将创建的对象作为this的上下文
const res = Func.apply(obj, args)
// 如果该函数没有返回对象,则返回this
return res instanceof Object ? res : obj
}
手写fetch拦截器
(function() {
let interceptor_req = [], interceptor_res = []
function c_fetch(url, init = {}) {
init.method = init.method || 'GET'
interceptor_req.forEach(interceptor => {
init = interceptor(init)
})
return new Promise((resolve, reject) => {
fetch(url, init).then(res => {
interceptor_res.forEach(interceptor => {
res = interceptor(res)
})
resolve(res)
}).catch(err => {
console.error(err)
})
})
}
c_fetch.interceptor = {
request: {
use: function(callback){
interceptor_req.push(callback)
}
},
response: {
use: function(callback){
interceptor_res.push(callback)
}
}
}
// export default c_fetch
})()
手写promise
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
queueMicrotask(() => {
this.value = value
this.resfns.forEach(fn => { //循环依次调用
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //将多次调用的then 方法保存到数组中,依次调用
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
instanceof
function isInstanceOf(instance, klass) {
let proto = instance.__proto__
let prototype = klass.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = proto.__proto__
}
}
// 测试
class Parent {}
class Child extends Parent {}
const child = new Child()
console.log(isInstanceOf(child, Parent), isInstanceOf(child, Child), isInstanceOf(child, Array));
随机生成颜色
function rgb(){//rgb颜色随机
var r = Math.floor(Math.random()*256);
var g = Math.floor(Math.random()*256);
var b = Math.floor(Math.random()*256);
var rgb = '('+r+','+g+','+b+')';
return rgb;
}
function color16(){//十六进制颜色随机
var r = Math.floor(Math.random()*256);
var g = Math.floor(Math.random()*256);
var b = Math.floor(Math.random()*256);
var color = '#'+r.toString(16)+g.toString(16)+b.toString(16);
return color;
}
异步并发数限制
/**
* 关键点
* 1. new promise 一经创建,立即执行
* 2. 使用 Promise.resolve().then 可以把任务加到微任务队列,防止立即执行迭代方法
* 3. 微任务处理过程中,产生的新的微任务,会在同一事件循环内,追加到微任务队列里
* 4. 使用 race 在某个任务完成时,继续添加任务,保持任务按照最大并发数进行执行
* 5. 任务完成后,需要从 doingTasks 中移出
*/
function limit(count, array, iterateFunc) {
const tasks = []
const doingTasks = []
let i = 0
const enqueue = () => {
if (i === array.length) {
return Promise.resolve()
}
const task = Promise.resolve().then(() => iterateFunc(array[i++]))
tasks.push(task)
const doing = task.then(() => doingTasks.splice(doingTasks.indexOf(doing), 1))
doingTasks.push(doing)
const res = doingTasks.length >= count ? Promise.race(doingTasks) : Promise.resolve()
return res.then(enqueue)
};
return enqueue().then(() => Promise.all(tasks))
}
// test
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i))
limit(2, [1000, 1000, 1000, 1000], timeout).then((res) => {
console.log(res)
})
异步串行 | 异步并行
// 字节面试题,实现一个异步加法
function asyncAdd(a, b, callback) {
setTimeout(function () {
callback(null, a + b);
}, 500);
}
// 解决方案
// 1. promisify
const promiseAdd = (a, b) => new Promise((resolve, reject) => {
asyncAdd(a, b, (err, res) => {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
// 2. 串行处理
async function serialSum(...args) {
return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))
}
// 3. 并行处理
async function parallelSum(...args) {
if (args.length === 1) return args[0]
const tasks = []
for (let i = 0; i < args.length; i += 2) {
tasks.push(promiseAdd(args[i], args[i + 1] || 0))
}
const results = await Promise.all(tasks)
return parallelSum(...results)
}
// 测试
(async () => {
console.log('Running...');
const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
console.log(res1)
const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
console.log(res2)
console.log('Done');
})()
call
Function.prototype.myCall = function (context = globalThis) {
// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖
const key = Symbol('key')
context[key] = this
// es5 可通过 for 遍历 arguments 得到参数数组
const args = [...arguments].slice(1)
const res = context[key](...args)
delete context[key]
return res
};
// 测试
const me = { name: 'Jack' }
function say() {
console.log(`My name is ${this.name || 'default'}`);
}
say.myCall(me)
apply
Function.prototype.myApply = function (context = globalThis) {
// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖
const key = Symbol('key')
context[key] = this
let res
if (arguments[1]) {
res = context[key](...arguments[1])
} else {
res = context[key]()
}
delete context[key]
return res
}
// 测试
const me = { name: 'Jack' }
function say() {
console.log(`My name is ${this.name || 'default'}`);
}
say.myApply(me)
bind
Function.prototype.myBind = function (context = globalThis) {
const fn = this
const args = Array.from(arguments).slice(1)
const newFunc = function () {
const newArgs = args.concat(...arguments)
if (this instanceof newFunc) {
// 通过 new 调用,绑定 this 为实例对象
fn.apply(this, newArgs)
} else {
// 通过普通函数形式调用,绑定 context
fn.apply(context, newArgs)
}
}
// 支持 new 调用方式
newFunc.prototype = Object.create(fn.prototype)
return newFunc
}
// 测试
const me = { name: 'Jack' }
const other = { name: 'Jackson' }
function say() {
console.log(`My name is ${this.name || 'default'}`);
}
const meSay = say.myBind(me)
meSay()
const otherSay = say.myBind(other)
otherSay()