JavaScript技术

前端手写

2021-07-21  本文已影响0人  Grandperhaps
  1. 节流 防抖
  2. 用xhr手写axios
  3. 函数柯里化
  4. 手写promise
  5. 手写reduce
  6. new
  7. 深拷贝
  8. string.indexOf()
    9.reduce实现map
  9. 深拷贝
  10. 柯里化
  11. 手写repeat函数
  12. 手写promise.all
  13. 手写apply bind
  14. 实现一个eventbus
  15. 发布订阅

1. 节流 防抖

        //节流:一个函数执行后,只有大于设定的执行周期后,才会执行第二次,
        //有个需要频繁出发的函数,出于性能优化,在规定的时间内,只让出发的第一次生效,后边的不生效
        /* fn被节流的函数 deley 规定时间*/
        function throttle(fn, delay) {
            //记录上一次函数出发时间
            let lastTime = 0
            return function() {
                //记录当前函数触法的时间
                let nowTime = Date.now()
                if (nowTime - lastTime > delay) {
                    fn.call(this)
                    lastTime = nowTime
                }
            }
        }
        document.onscroll = throttle(() => {
            console.log('触发成功!' + Date.now())
        }, 1000)

防抖

        //防抖函数:一个频繁出发的函数,在规定的某个时间内,只让最后一次生效,前边的不生效如:频繁点击按钮
        function debounce(fn, delay) {
            let timer = null
            return function() {
                //清楚上一次延时器
                clearTimeout(timer)
                    //重新设置新的延时器,
                timer = setTimeout(() => {
                    fn.call(this)
                }, delay)
            }
        }
        document.getElementById('btn').onclick = debounce(() => {
            console.log("触发了")
        }, 2000)

2. 用xhr手写axios(将原生的ajax封装成promise)

var  myNewAjax=function(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(data);
xhr.onreadystatechange=function(){
if(xhr.status==200&&readyState==4){
var json=JSON.parse(xhr.responseText);
resolve(json)
}else if(xhr.readyState==4&&xhr.status!=200){
reject('error');
}
}
})
}

3. 函数柯里化

function curry(fn,val){
  return function(){
    //转化为数组
    var args = Array.from(arguments)
    if(val){
      //参数拼接
      args = args.concat(val)
    }
    //fn.length 表示函数需要传入多少参数
    //递归调用 当参数相等停止递归
    if(fn.length > args.length){
      return curry(fn,args)
    }
    return fn.apply(null,args)
  }
}
function sum(a, b, c) {
  console.log(a + b + c);
}
const fn = curry(sum);
fn(1,2,3)
fn(1)(2)(3)

4. 手写promise


5. 手写reduce

Array.prototype.reduce = function(fn, init) {
    var arr = this   // this就是调用reduce方法的数组
    var total =  init || arr[0]   // 有初始值使用初始值
    // 有初始值的话从0遍历, 否则从1遍历
    for (var i = init ? 0 : 1; i < arr.length; i++) {
        total = fn(total, arr[i], i , arr)
    } 
    return total
}
var arr = [1,2,3]
console.log(arr.reduce((prev, item) => prev + item, 10)) 

6. new

  1. new的具体步骤
  2. 创建一个空对象 var obj = {}
  3. 修改obj.proto=Dog.prototype
  4. 只改this指向并且把参数传递过去,call和apply都可以
    根据规范,返回 null 和 undefined 不处理,依然返回obj
function _new(fn,...rest){
  //基于fn的prototype构建对象的原型
  const thisObj = Object.create(fn.prototype);
  //将thisObj作为fn的this,继承其属性,并获取返回结果为result
  const result = fn.apply(thisObj,rest);
  //根据result对象的类型决定返回结果
  return typeof result === "object" ? result : thisObj;
}

7. 深拷贝

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
        //排除forin继承父属性
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

8. string.indexOf()

String.prototype.indexO = function(st){
            // console.log(this.length);
            let str = this;
            var j = 0;
            let reflag;
            for(let i = 0;i< str.length;i++){
                if (str.charAt(i) == st.charAt(0)){
                    // console.log(str.charAt(i))
                    // console.log(st.charAt(0))
                    let re_selft = i;
                    let _self = i;
                    while(j<st.length){
                        if(str.charAt(_self)!= st.charAt(j)){
                            reflag = -1;
                            return reflag;
                        }
                        else{
                            reflag =  re_selft
                        }
                        _self++;
                        j++;
                    }
                }
            }
            return reflag
        }

9.reduce实现map

Array.prototype.mymap = function(fn,mapthis){
  var res = []
  var redthis = mapthis||null
  this.reduce(function(sum,val,index,arr){
    res.push(fn.call(redthis,val,index,arr))
  },null)
  return res
}
var arr = [1,2,3,5,1]
console.log(arr.mymap(val=>val*2));

10. 深拷贝

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
        //排除forin继承父属性
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

11. 柯里化

function curry(fn,val){
  return function(){
    //转化为数组
    var args = Array.from(arguments)
    if(val){
      //参数拼接
      args = args.concat(val)
    }
    //fn.length 表示函数需要传入多少参数
    //递归调用 当参数相等停止递归
    if(fn.length > args.length){
      return curry(fn,args)
    }
    return fn.apply(null,args)
  }
}
function sum(a, b, c) {
  console.log(a + b + c);
}
const fn = curry(sum);
fn(1,2,3)//6
fn(1)(2)(3)//6
fn(1,2)(3)//6

12. 手写repeat函数

        function sleep (func,wait,args) {
            return new Promise((resolve)=>{
                setTimeout(()=>{
                    func.apply(this,args);
                    resolve()
                },wait)
            })
        }
        function repeat(func, times, wait,args) {
            return async function () {
                for(i = 0;i<times;i++){
                await sleep(func,wait,args)
            }   
            }
        }
        function alert(){
            console.log('hellowWord');
        }
        const repeatFunc = repeat(alert, 4, 3000);
        repeatFunc()
        // 调用这个 repeatFunc ("hellworld"),会alert4次 helloworld, 每次间隔3秒

13. 手写promise.all

function myPromiseAll(arr) {  // 参数是一个iterable对象,一般是数组
  // 返回一个Promise实例
   return new Promise((resolve, reject) => {
      var index = 0
      var result = []
      function newData(i,data){
        result[i] = data
        if(++index === arr.length){
          resolve(result)
        }
       }
       if (arr.lenght == 0) {
         resolve()
       } else {
         for(let i = 0; i < arr.length ; i++){
           if(arr[i].then){
            arr[i].then(data=>{
              newData(i,data)
             },(err)=>{
               reject(err)
               console.log(arr);
               // 输出传入的数组中每一个 promise 执行后的状态和值
               return
             })
           } else {
             newData(i,arr[i])
           }
         }
       }  
   });
}

let p1 = new Promise((resolve, reject)=> {
  setTimeout(resolve, 2000, "P1 resolved");
})

let p2 = new Promise((resolve, reject)=> {
  setTimeout(reject, 3000, "P2 resolved");
})

let p3 = new Promise((resolve, reject)=> {
  setTimeout(resolve, 4000, "P3 resolved");
})

let pResult = myPromiseAll([p1,p2,p3]);
pResult.then(value=>{
  console.log(pResult);
  console.log(value);
},err=> {
  console.log(pResult); 
  console.log(err);
})
// 输入不仅仅只有Array
function promiseAll (args) {
  return new Promise((resolve, reject) => {
    const promiseResults = [];
    let iteratorIndex = 0;
    // 已完成的数量,用于最终的返回,不能直接用完成数量作为iteratorIndex
    // 输出顺序和完成顺序是两码事
    let fullCount = 0;
    // 用于迭代iterator数据
    for (const item of args) {
      // for of 遍历顺序,用于返回正确顺序的结果
      // 因iterator用forEach遍历后的key和value一样,所以必须存一份for of的 iteratorIndex
      let resultIndex = iteratorIndex;
      iteratorIndex += 1;
      // 包一层,以兼容非promise的情况
      Promise.resolve(item).then(res => {
        promiseResults[resultIndex] = res;
        fullCount += 1;
        // Iterator 接口的数据无法单纯的用length和size判断长度,不能局限于Array和 Map类型中
        if (fullCount === iteratorIndex) {
          resolve(promiseResults)
        }
      }).catch(err => {
        reject(err)
      })
    }
    // 处理空 iterator 的情况
    if(iteratorIndex===0){
      resolve(promiseResults)
    }
  }
  )
}

Race

function myPromiseRace(arr){
  return new Promise((resolve,reject)=>{
    arr.forEach(p=>{
      Promise.resolve(p).then(data=>{
        resolve(data)
      },err=>{
        reject(err)
      })
    })
  })
}
let p1 = new Promise((resolve, reject)=> {
  setTimeout(resolve, 2000, "P1 resolved");
})

let p2 = new Promise((resolve, reject)=> {
  setTimeout(resolve, 1000, "P3 resolved");
})
let pResult = myPromiseRace([p1,p2]);
pResult.then(value=>{
  console.log(pResult);
  console.log(value);
},err=> {
  console.log(pResult); 
  console.log(err);
})

14. 手写apply bind

bind

Function.prototype.mybind = function () {
  let args = Array.from(arguments);
  let thisArg = args.shift();
  let thisFunc = this;
  
  return function F() {
    newArgs = args.concat(Array.from(arguments));
    if (this instanceof F){
        return thisFunc.apply(this.newArgs)
    }
    return thisFunc.apply(thisArg, newArgs);
  }
}

apply

Function.prototype.myApply = function(context) {
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  context.fn = this
  let args = arguments[1]
  let result
  if (args) {
    result = context.fn(...args)
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

call

Function.prototype.myCall = function(context) {
  // 判断是否是undefined和null
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  context.fn = this
  let args = [...arguments].slice(1)
  let result = context.fn(...args)
  delete context.fn
  return result
}

15. 实现一个eventbus

function EventBus() {}

EventBus.prototype.on = function (name, callback) {
 //如果没有事件对象,新增一个
 if(!this._events){
  //创建一个干净的没有原型链的对象
  this._events = Object.create(null);
 }
 //如果没有这个事件的订阅,新增一个,如果有,push进去
 if(!this._events[name]){
  this._events[name] = [callback];
 }else{
  this._events[name].push(callback);
 }
}

EventBus.prototype.emit = function (name, ...args) {
 //发布的时候,如果有这个事件,循环执行所有这个订阅的方法
 if(this._events[name]){
  this._events[name].forEach(callback => {
   callback(...args);
  })
 }
}

EventBus.prototype.off = function (name) {
 //如果有这个事件的订阅,清除所有订阅
 if(this._events[name]){
  delete this._events[name];
 }
}

EventBus.prototype.once = function (name, callback) {
 let once = (...args) => {
  callback(...args);
  this.off(name);
 };
 this.on(name, once);
}

let eventBus = new EventBus();

eventBus.on('on', function (msg) {
 console.log(msg);
})
eventBus.once('once', function (msg) {
 console.log(msg);
})
eventBus.on('off', function (msg) {
 console.log(msg);
})
eventBus.emit('on', '发布on1')//发布on1
eventBus.emit('on', '发布on2')//发布on2
eventBus.emit('once', '发布once')//发布once
eventBus.emit('once', '发布once')
eventBus.emit('off', '发布off')//发布off
eventBus.off('off')
eventBus.emit('off', '发布off')

16. 发布订阅

class Event {
    // Events<String, Function[]>
    events = {};

    emit(type, ...args) {
        // 发布事件
        // 可以传递多个参数,每个事件处理函数都会被执行一次
        const listeners = this.events[type];
        for (const listener of listeners) {
            listener(...args);
        }
    }

    on(type, listener) {
        // 注册事件
        // 一个事件可以绑定多个事件处理函数
        this.events[type] = this.events[type] || [];
        this.events[type].push(listener);
    }
}

const e = new Event();

e.on("click", x => console.log(x.id));
e.emit("click", { id: 3 }); // 3
e.emit("click", { id: 4 }); // 4

17. 数组扁平化

1. reduce

function flatten(arr) {  
    return arr.reduce((result, item)=> {
        return result.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}

2. toString & split

function flatten(arr) {
    return arr.toString().split(',').map(function(item) {
        return Number(item);
    })
}

3. join & split
和上面的toString一样,join也可以将数组转换为字符串

function flatten(arr) {
    return arr.join(',').split(',').map(function(item) {
        return parseInt(item);
    })
}

4. 递归
递归的遍历每一项,若为数组则继续遍历,否则concat

function flatten(arr) {
    var res = [];
    arr.map(item => {
        if(Array.isArray(item)) {
            res = res.concat(flatten(item));
        } else {
            res.push(item);
        }
    });
    return res;
}

5. 扩展运算符
es6的扩展运算符能将二维数组变为一维

[].concat(...[1, 2, 3, [4, 5]]);  // [1, 2, 3, 4, 5]

function flatten(arr) {
   while(arr.some(item=>Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

18. 扁平数组转换为数

function getTree(arr, parent) {
  let temp = arr.slice()
  let newArr = []

  temp.forEach(item => {
    if (item.parentId === parent) {
      let obj = {}
      obj.id = item.id
      obj.parentId = item.parentId  // 这一块可以写一个深拷贝
      
      obj.children = getTree(temp, item.id)
      newArr = newArr.concat(obj)
    }
  })
  return newArr
}
上一篇下一篇

猜你喜欢

热点阅读