const arr = [1,2,[2,3],[5,6],7,0]
const fn = (arr, newArr) => {
arr.forEach(item => {
if(Array.isArray(item)) {
fn(item, newArr)
} else {
newArr.push(item)
}
})
return newArr
}
console.log(fn(arr, [])) // [1, 2, 2, 3, 5, 6, 7, 0]
方法2:flat()
flat()接受一个数值参数,表示需要拉平的层级,Infinity表示无限层级的拉平。
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
arr.flat(Infinity) // [1, 2, 2, 3, 5, 6, 7, 0]
方法3:reduce() 函数:遍历数组,每次返回上一次的处理后的值。reduce(function(prev,next){}, initVal), initVal 为prev的初始值,不传initVal,prev的初始值为数组的第一个元素,next为第二个数组元素。
// reduce 每次的prev都是[],所以采用数组的concat()
const fn2 = arr => {
return arr.reduce((prev,next) => {
return prev.concat(Array.isArray(next) ? fn2(next) : next)
},[])
}
-
防抖debounce
触发高频事件后n秒内会执行一次,如果n秒内再次触发,则重新计算时间进行执行。
即:触发高频事件每隔一定时间执行一次。
场景:防抖常应用于用户进行搜索输入节约请求资源,滚动事件等
const debounce = (fn,time = 500) => {
let timer = 0
return function() {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this)
}, time)
}
}
// 高频触发函数mousemove
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('触发函数')
box.addEventListener('mousemove', debounce(fn4))
-
节流throttle
n秒内触发高频事件只会执行一次, 所以节流会稀释函数的执行频率。
场景:节流常应用于防重复点击。
const throttle = (fn, time = 100) => {
let isFinished = false
return function() {
if(isFinished) {
clearTimeout(timer)
return
}
timer = setTimeout(() => {
fn.apply(this)
isFinished = true
}, time)
}
}
// 防重复点击
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('触发函数')
box.addEventListener('click', throttle(fn4))
-
arguments.callee
在函数内部,有两个特殊的对象:arguments 和 this。其中, arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
场景:使用递归的过程中会根据条件来调用函数本身,这样会导致一个问题,函数内部存在一个我们自定义的函数名,不利于再次封装。
// 使用函数名
const fn = function(num) {
if(num > 1) {
return num * fn(num - 1)
} else {
return 1
}
}
// 使用arguments.callee
const fn = function(num) {
if(num > 1) {
return num * arguments.callee(num - 1)
} else {
return 1
}
}
-
函数珂里化
指的是将一个接受多个参数的函数变为接受一个参数,返回一个函数的固定形式,便于再次调用。
// 求和:curry(1)(2)(3)(4) 及curry(2)(2,3,4)(5)
const curry = function() {
const _arg = [...arguments]
const fn = function() {
_arg.push(...arguments)
return fn
}
fn.toString = () => _arg.reduce((prev, next) => prev + next)
return fn
}
-
原型(prototype)、原型链和原型继承
- 所有引用类型(函数,数组,对象)都拥有proto属性(隐式原型)
- 所有函数拥有prototype属性(显式原型)(仅限函数)
- 原型链的实现:查找属性,如果本身没有,则会去__ proto __ 中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有 __ proto __,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined。
-
构造函数都有一个prototype属性指向原型对象,原型对象通过constructor属性指向构造函数本身。
实例对象都有一个__ proto __属性指向原型对象 - 实例对象的__ proto __指向构造函数的prototype,从而实现继承。
prototype对象相当于特定类型所有实例对象都可以访问的公共容器 - 继承:
(1)原型链继承
(2)call和apply。区别call(this,p1,p2,p3,...),apply(this.[...params])第二个开始为参数集合
(3)方法继承:深度拷贝原型,然后更改constructor属性
person._ proto _ = Object.create(People.prototype)
peson.constructor = Person
或者
Person.prototype = Object.create(People.prototype)
peson.constructor = Person
https://blog.csdn.net/yucihent/article/details/79424506
https://zhuanlan.zhihu.com/p/35790971
- new 操作符的实现
思路:新创建一个空对象,新对象原型赋值构造函数的原型,执行构造函数并绑定this到新对象。(新对象拥有构造函数及原型上的属性和方法)
function _MyNew(obj) {
// 创建一个空对象
let newObj = Object.create()
// 设置空对象的原型
newObj.__proto__ = obj.prototype
// 使用apply绑定this,执行构造函数
const res = obj.apply(newObj)
const isObject = typeof res === 'object' && res !== null;
const isFunction = typeof res === 'function';
return isObject || isFunction ? res : obj;
}
-
宏任务与微任务
Javascript 是单线程脚本语言。分为同步任务(立即执行)、异步任务(不会立即执行),异步任务分为宏任务、微任务,异步任务进过事件注册模块将回调添加到任务队列进行处理。
(1)宏任务
常见的有:
setTimeout
setInterval
requireAnimationFrame请求动画帧(浏览器独有)
UI render(浏览器独有)
setImmediate(node独有)
I/O
(2)微任务
常见的有:
Promise.then()
Process.nextTick(node独有)
Object.observe
MutationObserve
(1)当一个异步任务进入栈的时候,主线程会判断是同步还是异步任务,如果是同步任务,则立即执行;如果是异步任务,则将该任务交给异步处理模块处理,当异步处理完到触发条件的时候,根据任务的类型,将回调压入队列之中。如果是宏任务,则新增一个宏任务,任务队列中的宏任务可以有多个;如果是微任务,则直接压入微任务队列。
(2)当两个宏任务队列的时候,第一个宏任务队列中有一个微任务:即当在执行宏任务的时候,遇到微任务则将微任务加入微任务队列,最后剩下一个微任务和一个宏任务,此时会先执行微任务,再执行宏任务。
(3)宏任务与微任务在浏览器和node中的执行有所差异
浏览器:执行宏任务,然后执行宏任务对应的微任务,重复进行。由于执行栈的入口为script,而全局任务为宏任务,所以当栈为空的时候,同步任务执行完毕,会先执行微任务队列。微任务执行完毕,会读取宏任务队列中最前的任务,执行宏任务的过程中,如果遇到微任务,则依次加入微任务队列,栈空后,再次读取微任务,依次类推。
node:执行完全部的宏任务,然后执行微任务。
console.log('1')
setTimeout(()=>{
console.log('2');
new Promise(resolve=>{
console.log('3')
resolve();
}).then(()=>{
console.log('4')
})
},0)
new Promise(resolve=>{
console.log('5')
resolve();
}).then(()=>{
console.log('6')
})
setTimeout(()=>{
console.log('7');
},0)
setTimeout(()=>{
console.log('8');
new Promise(resolve=>{
console.log('9')
resolve();
}).then(()=>{
console.log('10')
})
},0)
new Promise(resolve=>{
console.log('11')
resolve();
}).then(()=>{
console.log('12')
})
console.log('13');
答案://1, 5,11,13,6,12,2,3,4,7,8,9,10
转 https://blog.csdn.net/qq_42833001/article/details/87252890
- 渲染几万条数据不卡住页面借助请求动画帧requestAnimationFrame
/**
功能: 匀速滚动到页面顶部
@dom: 事件源DOM,
@speed: 总份数
*/
function scrollTopHandler(dom, speed = 500) {
// 滚动的高度
let doc_scrollTop
let i = 0
// requestAnimationFrame 的执行函数
const gotoTop = function() {
// 将滚动高度分为1000份, 每份为unit的高度
const unit = doc_scrollTop / speed
// 每次执行10份unit的距离
i += 10
// 计算每次滚动的高度
const scrollTop = doc_scrollTop - i * unit > 0 ? doc_scrollTop - i * unit : 0
document.documentElement.scrollTop = scrollTop
// 判断执行分数i小于speed 总分数,则继续执行
if (i <= speed) {
window.requestAnimationFrame(gotoTop)
} else {
// 重置
i = 0
}
}
// 点击事件监听
const handler = function() {
doc_scrollTop = document.documentElement.scrollTop
window.requestAnimationFrame(gotoTop)
}
dom.addEventListener('click',handler,false)
}
// 调用方法
const dom = document.querySelector('#gotoTop')
scrollTopHandler(dom)
- ** 高频算法**
算法面试必备
经典冒泡排序
function bubbleSort(arr) {
for(let i = 0; i < arr.length - 1; i++){
for(let j = i + 1; j <= arr.length - 1; j++) {
if(arr[i] > arr[j]) {
const tempVal = arr[i]
arr[i] = arr[j]
arr[j] = tempVal
}
}
}
return arr
}
双指针-快速排序
思想:来自于快速排序,在一个有序的数组(从小到大)中最左边一定是最小值,最右边是最大值。我们可将最小值与最大值相加与目标值进行比较,如果两数之和大于目标值,我们就让最大值小一点(读取第二个最大值),如果两数之和小于目标值,我们就让最小值大一点(读取第二个最小值),如果两数之和刚好等于目标值,保存最大值,最小值,并且让最大值小一点,最小值大一点。需要注意的是前提条件是数组必须有序!
function sum2(arr,tartgetVal) {
const resulutArr = []
const sortArr = arr.sort(function(a,b){
return a - b
})
let start = 0
let end = sortArr.length - 1
while(start < end) {
if (sortArr[start] + sortArr[end] === tartgetVal) {
resulutArr.push([sortArr[start], sortArr[end]])
start += 1
end -= 1
} else if (sortArr[start] + sortArr[end] >= tartgetVal) {
end -= 1
} else {
start += 1
}
}
console.log(resulutArr)
return resulutArr
}
const arr = [1, 4, 3, 2, 6, 5]
sum2(arr,6)
function sum3(arr, targetVal) {
const sortArr = arr.sort((a,b) => {
return a - b;
})
console.log(sortArr)
let result = []
const len = sortArr.length - 3
for(let i = 0; i < sortArr.length -3; i++) {
let left = i + 1, right = sortArr.length - 1
while(left < right) {
if(sortArr[i] + sortArr[left] + sortArr[right] === targetVal) {
result.push([sortArr[i], sortArr[left], sortArr[right]])
left += 1
right -= 1
} else if (sortArr[i] + sortArr[left] + sortArr[right] > targetVal) {
right -= 1
} else {
left += 1
}
}
}
console.log(result)
return result
}
const arr = [1, 4, 3, 2, 6, 5, 9, 8, 21, 22, 34, 10, 14]
sum3(arr, 10)
//
[Array(3), Array(3), Array(3)]
0: [1, 3, 6]
1: [1, 4, 5]
2: [2, 3, 5]