手撕js(自用

145 阅读6分钟
  • 防抖 (应用?)
function debounce(fn,delay){
    let timer=null
    return function(...args){
      let context=this
      if(timer)clearTimeout(timer)
      timer=setTimeout(function(){
        fn.apply(context,args)
      },delay)
   }
}
因为是定时器所以this提前要保存下来
如果没有特殊指向,setIntervalsetTimeout的回调函数中this的指向都是window 

-节流

function throttle(fn,interval){
    let flag=true
    return function(...args){
       let context=this
       if(!flag)return
       flag=false
       setTimeout(()=>{
         fn.apply(context,args)
         flag=true
       },inteval)
     }
 }

-防抖节流结合 (有时候防抖可能一次响应都没有执行但我们想到了固定时间段之后会执行这个函数)

function throttle(fn,delay){
    let last=0,timer=null
    return function(...args){
       let context=this
       let now=new Date()
       if(now-last<delay){
         clearTimeout(timer)
         setTimeout(function(){
            last=now
            fn.apply(context,args)
         },delay)
       }else{
          last=now
          fn.apply(context,args)
       }
    }
 }

-图片懒加载

<img src="default.jpg" data-src="http://www.xxx.com/target.jpg" />

let img=document.getElementsByTagName("img")
let num=img.length
let count=0
lazyload()
window.addEventListener('scroll',lazyload)
function lazyload(){
    let viewHeight=document.documentElement.clientHeight
    let scrollTop=document.documentElement.scrollTop ||document.body.scrollTop
    for(let i=count;i<num;i++){
       if(img[i].offsetTop<scrollHeight+viewHeight){
       if(img[i].getAttribute("src")!=='default.jpg')continue
       img[i].src=img[i].getAttribute('data-src')
       count++
     }
   }
}
//offsetTop不是动态根据照片的高度决定的 而是他本身距离网页顶端的距离 和视口无关
当然最好对scroll事件做节流处理
window.addEventListener('scroll',throttle(lazyload,200));

还有一种方式判断图片是否出现在当前视口 即dom元素的getBoundingClientReact()

function lazyload(){
   for(let i=count;i<num;i++){
     if(img[i].getBoundingRect().top<document.documentElement.clientHeight){
     if(img[i].getAttribute('src')!=='default.jpg')continue
     img[i].src=img[i].getAttribute('data-src')
     count++
    }
  }
}
也就是说这个api获得的top是根据此照片当前距离视口的顶端

-数组去重

let map=new Map()
arr.indexOf(n)  -1 or index
[...new Set(arr)]
arr.includes(n)

-基于promise封装ajax

function ajax(methods,url){
    return new Promise((resolve,reject)=>{
      const xhr=new XMLHttpRequest()
      xhr.open(methods,url,true)
      xhr.onreadystatechange=function(){
         if(xhr.readyState==4){
           if(xhr.status==200){
              resolve(xhr.responseText)
           }else if(xhr.status==404){
              reject(new Error('404')
           }
         }else{
              reject('error')
      }
      xhr.send()
     })
 }

-手动实现jsonp跨域

function jsonp(url,data,callback){
    data=[...data,callback]
    let arr=[]
    for(let key in data){
      arr.push(key+'='+data[key])
    }
    let params=arr.join('&')
    let script=document.createElement('script')
    script.src=url+'?'+params
    document.body.appendChild(script)
    return new Promise((resolve,reject)=>{
       try{
          resolve(data)
       }catch(err){
          reject(err)
       }finally{
           document.body.removeChild(script)
       }
     })
  }

-手动实现sleep(休眠 我就是休眠)

function sleep(time){
    return new Promise(resolve=>setTimeout(resolve,time))
}
function say(name){
   console.log(${name})
}
async function task(){
   await sleep(3000)
   //他是等到上面函数执行完再向下 异步  如果只是sleep函数没有await的话 那并不会休眠3s 下面的say是当前宏任务一部分会先执行 await后面必须忒是异步任务promise才可
   say('nnn')
}
task()

-手动实现reduce

arr.map() 返回新数组 表示映射 对数组中每一项运行给定函数,返回每次函数调用的结果组成的数组
arr.filter()返回新数组 过滤功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组
erery()返回布尔值 判断数组中每一项是否满足条件,只有所有项都满足条件,才会返回true
let flag=arr.some(function(x){
   return x<3
 })
forEach()为每个数组元素调用一次函数 不返回什么东西
reduce()返回一个值 在每个数组元素上运行函数,一次生成单个值,不会改变原始数组

reduce:初始值不传的时候会默认使用数组中第一个元素
函数返回的结果回座位下一次循环的prev
1.上一次调用回调时返回的值
2.参 正在处理的元素
3.正在处理的元素的索引
4.正在遍历的集合对象
Array.rpototype.myReduce=function(fn,prev){
   for(let i=0;i<this.length;i++){
       if(typeof prev =='undefined'){
         prev=fn(this[i],this[i+1],i+1,this)
         i++
       }else{
         prev=fn(prev,this[i],i,this)
       }
   }
   return prev
}
let sum=[1,2,3].myReduce((pre,cur)=>{
   return pre+cur
})

-手动实现柯里化函数

一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数。
高阶函数就是 把函数作为参数传入或者返回一个函数

柯里化函数是高阶函数的一个特殊用法
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

function add(a,b,c,d){
   return a+b+c+d
}
function currying(fn,...args){
   if(fn.length==args.length){
      return fn(...args)
   }else{
      return function(...newArags){
         return curring(fn,...args,...newArgs)
      }
    }
 }
let addSum=curring(add)(1,2)
addSum(3)(4)
感觉还是解决了四个数加和的方式 并且最后解决的方式还是add(1,2,3,4)只不过可以分批了 哦 万一我们此时不知道所有参数需要分批获得参数 可以这么解决哦 虽然还是一样的add执行 而且只有四参 当然add可以修改for循环改成随意参数

函数的length:
length是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。   

-es5实现继承

寄生组合继承
function Children(name,age){
    Parent.call(this,name)
    this.age=age
}
function Parent(name){
   this.name=name
}
Parent.prototype.getName(){
   console.log(this.name)
}
Children.prototype=Object.create(Parent.prototype)
Children.prototype.constructor=Children
let c=new Children('a',18)
c.getName()


//js的编译顺序是啥来?
其实也可以用class 实现继承 super()
当然function 这种也不错

-手动实现发布订阅模式 (每次event.emit发布 就会触发一次event.on注册)

class EventEmitter{
  constructor(){
     this.events={}
  }
  on(eventName,callback){
     if(!this.events[eventName]){
        this.event[eventName]=[callback]
     }else{
        this.events[eventName].push(callback)
     }
   }
   emit(eventName){
      this.events[eventName]&&this.events[eventName].forEach(cb=>cb())
      }
  }
  let em=new EventEmitter()
  function f1(){
    console.log('a')
  }
  function f2(){
    console.log('aa')
  }
  em.on('task',f1)
  em.on('task',f2)
  em.emit('task')

-手动实现观察者模式

好像在vue双向绑定中也用到了这种模式
观察者模式是基于发布订阅模式的
观察者需要放到被观察者中
calss subject{
   constructor(name){
     this.name=name
     this.state='happy'
     this.observers=[]
   }
   attach(observer){
      this.observer.push(observer)
   }
   setState(newState){
      this.state=newState
      this.observers.forEach(observer=>observer.update(this));
      }
  }
  class Observer{
     constructor(name){
       this.name=name
     }
     update(student){
        console.log(student.name+student.state)
     }
  }
  let stu=new Student()
  let teacher=new Observer()
  let dad=new Observer()
  student.attach(teacher)
  student.attach(dad)
  stu.setState('a')
  
  attach:附加的中文意思

-手动实现Object.freeze(冻结一个对象 freeze不可修改 不可删除 不可写 不可读

Object.seal 阻止添加新属性并将所有现有属性标记为不可配置。配置是不可以的 但是可以改变属性值
使用`Object.freeze()`冻结的对象中的现有属性值是不可变的。用`Object.seal()`密封的对象可以改变其现有属性值。

他的受众就是对象
function myFreeze(obj){
   if(obj instanceof Object){
     for(let key in obj){
       if(obj.hasOwnProperty(key)){
         Object.defineProperty(obj,key,{
           writable:false
          })
         Object.seal(obj)
         myFreeze(obj[key])
    }
  }
 }
}
Object.freeze()有局限 面对
let obj={
    arr:[1,2,2]
}
Object.freeze(obj)
obj.arr[1]=3
console.log(obj) 他变了没有报错 是 1 3 2
所以如果冷冻的对象属性的值是一个复杂数据类型那可以修改成功 array是对象
注意一下   

-手动实现Promise.all

let res=[]
res[0]=1
res[1]=1
console.log(res.toString())
1 1 也就是说数组一开始不说长度但我可以顺着声明就没事
nb

Promise.myAll=(arr)=>{
   return new Promise((resolve,reject)=>{
      if(arr.length==0)resolve([])
      else{
       let res=[],cnt=0
       for(let i=0;i<arr.length;i++){
         if(arr[i] instanceof Promise){
           arr[i].then(data=>{
             res[i]=arr[i]
             if(++cnt==arr.length){
               resolve(res)
            },err=>{
               reject(err)
            })
         }else{
            res[i]=arr[i]
            if(++cnt==arr.length){
              resolve(res)
            }
         }
       }
     }
   })
}
all方法返回的是所有的promsie数组且安装传入数组的顺序
-   如果传入的参数是一个空的可迭代对象,则返回一个**已完成(already resolved)** 状态的 [`Promise`]
-   如果传入的参数不包含任何 `promise`,则返回一个**异步完成(asynchronously resolved)**  [`Promise`]
-   其它情况下返回一个**处理中(pending)** 的[`Promise`]。这个返回的 `promise` 之后会在所有的 `promise` 都完成或有一个 `promise` 失败时**异步**地变为完成或失败。


//以下是自己想叭叭的
在全局环境下没被定义的变量var将会自动定义为var

var b=10
function b(){
    b=20
    console.log(b)
}
b()
b不是一个方法 error

**1. async声明的函数只是把该函数的return包装了,使得无论如何都会返回promise对象(非promise会转化为promise{resolve}),除此之外与普通函数没有不同,没有特殊待遇。\
2. await声明只能用在async函数中。当执行async函数时,遇到await声明,会先将await后面的内容按照'平常的执行规则'执行完(就执行完await那一句),执行完后立马跳出async函数,去执行主线程其他内容,等到主线程(当前宏任务)执行完再回到await处继续执行后面的内容。
then只是个预告 并不会立即执行 等promise成功了才会自己调用 回调嘛**
await 之后那个是promise 他会执行promise内容但是promise.then()的内容是等当前宏任务完成之后 才默认promise 调用resolve了 才会去把then放到微任务队列中

async返回的是一个promise 

//然后打印出“promise start..”接下来会把返回的这promise放入promise队列(Promise的Job Queue),继续执行打印“test end...”,等本轮事件循环执行结束后,又会跳回到async函数中(test函数),等待之前await 后面表达式的返回值