防抖
原理
事件高频触发后,n秒内函数只会执行一次,若n秒内事件再次触发,则重新计时,总之就是要等触发完事件 n 秒内不再触发事件,函数才执行
实现
// 基本逻辑function debounce(){let timer = null; // 消除之前可能存在的定时任务if(timer) timer = nulltimer = setTimeout(()=>{console.log('function called')timer = null // 执行完毕后,清楚定时任务},500)}// 封装防抖函数function(callback, waitingTime = 500){let timer = null;return function (){if(timer) timer = null // 消除之前可能存在的定时任务timer = setTimeout(()=>{callback.apply(this,...arguments)timer = null // 执行完毕后,清楚定时任务},waitingTime)}}
节流
原理
实现
function throttle(callback,waitingTime,callImmediately){let timer = null,isFirstTime = true;return function(){if(isFisrtTime){callback.aplly(this,arguments)}else{if(timer) returntimer = setTimeout(()=>{callback.aplly(this,arguments)timer = null},waitingTime)}}}
模拟new运算符
原理
- 新建一个空对象- 链接到原型- 绑定this- 返回该对象
实现
function myNew() {// 1.新建一个空对象let obj = {}// 2.获得构造函数let con = [].shift.call(arguments)// 3.链接原型,实例的 __proto__ 属性指向构造函数的 prototypeobj.__proto__ = con.prototype// 4.绑定this,执行构造函数let res = con.apply(obj, arguments)// 5.返回新对象return typeof res === 'object' ? res : obj}function Person(name) {this.name = name}let person = myNew(Person,'nanjiu')console.log(person) //{name: "nanjiu"}console.log(typeof person === 'object') //trueconsole.log(person instanceof Person) // true
模拟instanceof
instanceof 用于检测构造函数的prototype是否在实例的原型链上,需要注意的是instanceof只能用来检测引用数据类型,对于基本数据检测都会返回false
原理
通过循环检测实例的proto属性是否与构造函数的prototype属性相等
实现
/*** instanceof 用于检测构造函数的prototype是否在实例的原型链上*/function myInstanceof(left, right) {// 先排除基本数据类型if(typeof left !== 'object' || left === null) return falselet proto = left.__proto__while(proto) {if(proto === right.prototype) return trueproto = proto.__proto__}return false}function Person() {}let person = new Person()console.log(myInstanceof(person,Person)) // true
模拟Function.prototype.apply()
实现
Function.prototype.myApply = function(context) {var context = context || window // 获取需要绑定的thiscontext.fn = this // 获取需要改变this的函数const arg = arguments[1] // 获取传递给函数的参数if(!(arg instanceof Array)) {throw Error('参数需要是一个数组')}const res = context.fn(...arg) // 执行函数delete context.fn // 删除该方法return res // 返回函数返回值}function say(a,b,c) {console.log(this.name,a,b,c)}say.myApply({name:'nanjiu'},[1,2,3]) //nanjiu 1 2 3say.apply({name:'nanjiu'},[1,2,3]) //nanjiu 1 2 3
模拟Function.prototype.call()
实现
Function.prototype.myCall = function(context) {var context = context || window // 获取需要改变的thiscontext.fn = this // 获取需要改变this的函数const args = [...arguments].slice(1) // 获取参数列表const res = context.fn(...args) // 将参数传给函数并执行delete context.fn // 删除该方法return res // 返回函数返回值}function say(a,b,c) {console.log(this.name,a,b,c)}say.myCall({name:'nanjiu'},1,2,3) //nanjiu 1 2 3say.call({name:'nanjiu'},1,2,3) //nanjiu 1 2 3
模拟Function.prototype.bind()
实现
Function.prototype.myBind = function(context) {var context = context || window //获取需要改变的thiscontext.fn = this // 获取需要改变this的函数//获取函数参数const args = [...arguments].slice(1)// 与apply,call不同的是这里需要返回一个函数return () => {return context.fn.apply(context,[...args])}}function say(a,b,c) {console.log(this.name,a,b,c)}say.bind({name: 'nanjiu'},1,2,3)() //nanjiu 1 2 3say.myBind({name: 'nanjiu'},1,2,3)() //nanjiu 1 2 3
模拟Array.prototype.forEach()
实现
Array.prototype.myForEach = function(callback, context) {const arr = this // 获取调用的数组const len = arr.length || 0let index = 0 // 数组下标while(index < len) {callback.call(context ,arr[index], index)index++}}let arr = [1,2,3]arr.forEach((item,index) => {console.log(`key: ${index} - item: ${item}`)})console.log('----------')arr.myForEach((item,index) => {console.log(`key: ${index} - item: ${item}`)})/*** key: 0 - item: 1key: 1 - item: 2key: 2 - item: 3----------key: 0 - item: 1key: 1 - item: 2key: 2 - item: 3*/
模拟Array.prototype.map()
实现
/*** map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。*/Array.prototype.myMap = function(callback, context) {const arr = this,res = []const len = arr.length || 0let index = 0while(index < len) {res.push(callback.call(context, arr[index], index))index ++}return res // 与forEach不同的是map有返回值}const arr = [1,2,3]let res1 = arr.map((item,index) => {return `k:${index}-v:${item}`})let res2 = arr.myMap((item,index) => {return `k:${index}-v:${item}`})console.log(res1) // [ 'k:0-v:1', 'k:1-v:2', 'k:2-v:3' ]console.log(res2) // [ 'k:0-v:1', 'k:1-v:2', 'k:2-v:3' ]
模拟Array.prototype.filter()
实现
/*** `filter()` 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。*/Array.prototype.myFilter = function(callback, context) {const arr = this,res = []const len = arr.lengthlet index = 0while(index < len) {if(callback.call(context,arr[index],index)) {res.push(arr[index])}index ++}return res}const arr = [1,2,3]let res1 = arr.filter((item,index) => {return item<3})let res2 = arr.myFilter((item,index) => {return item<3})console.log(res1) // [ 1, 2 ]console.log(res2) // [ 1, 2 ]
函数柯里化
实现
function curry(fn, curArgs) {const len = fn.length // 需要柯里化函数的参数个数curArgs = curArgs || []return function() {let args = [].slice.call(arguments) // 获取参数args = curArgs.concat(args) //拼接参数// 基本思想就是当拼接完的参数个数与原函数参数个数相等才执行这个函数,否则就递归拼接参数if(args.length < len) {return curry(fn, args)}else{return fn.apply(this, args)}}}let fn = curry(function(a,b,c){console.log([a,b,c])})fn(1,2,3) // [ 1, 2, 3 ]fn(1,2)(3) // [ 1, 2, 3 ]fn(1)(2,3) // [ 1, 2, 3 ]fn(1)(2)(3) // [ 1, 2, 3 ]
类数组转数组
类数组是具有length属性,但不具有数组原型上的方法。常见的类数组有arguments、DOM操作方法返回的结果。
实现
function translateArray() {//方法一:Array.fromconst res1 = Array.from(arguments)console.log(res1 instanceof Array, res1) // true [ 1, 2, 3 ]// 方法二:Array.prototype.slice.callconst res2 = Array.prototype.slice.call(arguments)console.log(res2 instanceof Array, res2) // true [ 1, 2, 3 ]// 方法三:concateconst res3 = [].concat.apply([],arguments)console.log(res3 instanceof Array, res3) // true [ 1, 2, 3 ]// 方法四:扩展运算符const res4 = [...arguments]console.log(res4 instanceof Array, res4) // true [ 1, 2, 3 ]}translateArray(1,2,3)
实现深拷贝
const isComplexDataType = (obj) => (typeof obj === 'object' && obj !== null);function deepClone(origin , hash = new WeakMap()){if(isComplectDataType(obj)){//判断date、RegExp类型对象if(instanceof Date){return new Date(obj)}if(instanceof Regxp){return new Regxp(obj)}// 判断hash表中是否已有缓存,避免自身引用导致的循环递归if(hash.get(obj)) return hash.get(obj)//获取属性描述符,然后创建新对象let descriptors = Object.getOwnPropertyDescriptors(obj);let newObj = Object.create(Object.getPrototypeOf(obj) ,descriptors);//可以创建对象和数组hash.set(obj, newObj);for(let key of Reflect.ownKeys(obj)){newObj[key] = isComplexDataType(origin[key]) ? deepClone(origin[key]) : origin[key]}return newObj}else{return obj}}
实现AJAX
过程
- 创建XMLHttpRequest对象
- 打开链接 (指定请求类型,需要请求数据在服务器的地址,是否异步i请求)
- 向服务器发送请求(get类型直接发送请求,post类型需要设置请求头)
接收服务器的响应数据(需根据XMLHttpRequest的readyState属性判定调用哪个回调函数)
实现
function ajax(url, method, data=null) {const xhr = XMLHttpRequest() // 咱们这里就不管IE低版本了// open()方法,它接受3个参数:要发送的请求的类型,请求的url和是否异步发送请求的布尔值。xhr.open(method, url ,false) // 开启一个请求,当前还未发送xhr.onreadyStatechange = function() {if(xhr.readyState == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){alert(xhr.responseText);} else {console.log("Request was unsuccessful: " + xhr.status);}}}if(method === 'post') {xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");}xhr.send(data) // get请求,data应为null,参数拼接在URL上}
多维数组扁平化
function flat(arr) {const res = []// 递归实现const stack = [...arr] // 复制一份while(stack.length) {//取出复制栈内第一个元素const val = stack.shift()if(Array.isArray(val)) {// 如果是数组,就展开推入栈的最后stack.push(...val)}else{// 否则就推入res返回值res.push(val)}}return res}const arr = [1,[2],[3,4,[5,6,[7,[8]]]]]console.log(flat(arr)) //[1, 2, 3, 4, 5, 6, 7, 8]// 调用flatconst arr = [1,[2],[3,4,[5,6,[7,[8]]]]]console.log(arr.flat(Infinity)) //[1, 2, 3, 4, 5, 6, 7, 8]
setTimeout 模拟 setInterval
递归调用setTimeout
function mySetInterval(callback, delay) {let timer = nulllet interval = () => {timer = setTimeout(()=>{callback()interval() // 递归}, delay)}interval() // 先执行一次return {id: timer,clear: () => {clearTimeout(timer)}}}let time = mySetInterval(()=>{console.log(1)},1000)setTimeout(()=>{time.clear()},2000)
setInterval 模拟 setTimeout
setInterval执行一次后将setInterval清除即可
function mySetTimeout(callback, delay) {let timer = nulltimer = setInterval(()=>{callback()clearInterval(timer)},delay)}mySetTimeout(()=>{console.log(1)},1000)
sleep
实现一个函数,n秒后执行指定函数
function sleep(func, delay) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(func())}, delay)})}function say(name) {console.log(name)}async function go() {await sleep(()=>say('nanjiu'),1000) //过一秒打印nanjiuawait sleep(()=>say('前端南玖'),2000) // 再过两秒打印前端南玖}go()
数组去重的多种实现方式
set
let arr = [1,2,3,2,4,5,3,6,2]function arrayToHeavy1(arr) {return [...new Set(arr)]}console.log(arrayToHeavy1(arr)) //[ 1, 2, 3, 4, 5, 6 ]
indexOf
function arrayToHeavy2(arr) {let newArr = []for(let i=0; i<arr.length; i++) {if(newArr.indexOf(arr[i]) == -1){newArr.push(arr[i])}}return newArr}console.log(arrayToHeavy2(arr)) //[ 1, 2, 3, 4, 5, 6 ]
filter
function arrayToHeavy3(arr) {return arr.filter((item, index) => {return arr.indexOf(item) == index})}console.log(arrayToHeavy3(arr)) //[ 1, 2, 3, 4, 5, 6 ]
include
function arrayToHeavy5(arr) {let res = []for(let i=0; i<arr.length; i++) {if(!res.includes(arr[i])) {res.push(arr[i])}}return res}console.log(arrayToHeavy5(arr)) //[ 1, 2, 3, 4, 5, 6 ]
解析url参数
function queryData(key) {let url = window.location.href,obj = {}let str = url.split('?')[1] // 先拿到问号后面的所有参数let arr = str.split('&') // 分割参数for(let i=0; i< arr.length; i++) {let kv = arr[i].split('=')obj[kv[0]] = decodeURIComponent(kv[1])}console.log(url,obj)// {a: '1', b: '2', c: '3', name: '南玖'}return obj[key]}//http://127.0.0.1:5500/src/js/2022/%E6%89%8B%E5%86%99/index.html?a=1&b=2&c=3console.log(queryData('name')) // 南玖function getURL(){let queryStr = window.location.href.split('?')[1],queryObj = {};let arr = queryStr.split('&');for(let i = 0;i < arr.length; i++){let singleQueryArr = arr[i].split('=');queryObj[singleQueryArr[0]] = singleQueryArr[1]}return queryObj}
斐波那契数列
基础版
function fib(n) {if(n == 0) return 0if(n == 1 || n == 2) return 1return fib(n-1) + fib(n-2)}// console.log(fib(4)) //F(4)=F(3)+F(2)=F(2)+F(1)+F(2)=1+1+1=3let t = +new Date()console.log(fib(40)) //102334155console.log(+new Date()-t) //783ms
优化版
function fib2(n) {if(fib2[n] !== undefined) return fib2[n]if(n == 0) return 0if(n == 1 || n == 2) return 1const res = fib2(n-1) + fib2(n-2)fib2[n] = resreturn res}let t1 = +new Date()console.log(fib2(40)) //102334155console.log(+new Date()-t1) //5ms
