前端手寫

  1. 節(jié)流 防抖
  2. 用xhr手寫axios
  3. 函數(shù)柯里化
  4. 手寫promise
  5. 手寫reduce
  6. new
  7. 深拷貝
  8. string.indexOf()
    9.reduce實(shí)現(xiàn)map
  9. 深拷貝
  10. 柯里化
  11. 手寫repeat函數(shù)
  12. 手寫promise.all
  13. 手寫apply bind
  14. 實(shí)現(xiàn)一個(gè)eventbus
  15. 發(fā)布訂閱

1. 節(jié)流 防抖

        //節(jié)流:一個(gè)函數(shù)執(zhí)行后包个,只有大于設(shè)定的執(zhí)行周期后粹庞,才會(huì)執(zhí)行第二次贩耐,
        //有個(gè)需要頻繁出發(fā)的函數(shù)蚓土,出于性能優(yōu)化,在規(guī)定的時(shí)間內(nèi)桥帆,只讓出發(fā)的第一次生效医增,后邊的不生效
        /* fn被節(jié)流的函數(shù) deley 規(guī)定時(shí)間*/
        function throttle(fn, delay) {
            //記錄上一次函數(shù)出發(fā)時(shí)間
            let lastTime = 0
            return function() {
                //記錄當(dāng)前函數(shù)觸法的時(shí)間
                let nowTime = Date.now()
                if (nowTime - lastTime > delay) {
                    fn.call(this)
                    lastTime = nowTime
                }
            }
        }
        document.onscroll = throttle(() => {
            console.log('觸發(fā)成功!' + Date.now())
        }, 1000)

防抖

        //防抖函數(shù):一個(gè)頻繁出發(fā)的函數(shù)老虫,在規(guī)定的某個(gè)時(shí)間內(nèi)叶骨,只讓最后一次生效,前邊的不生效如:頻繁點(diǎn)擊按鈕
        function debounce(fn, delay) {
            let timer = null
            return function() {
                //清楚上一次延時(shí)器
                clearTimeout(timer)
                    //重新設(shè)置新的延時(shí)器祈匙,
                timer = setTimeout(() => {
                    fn.call(this)
                }, delay)
            }
        }
        document.getElementById('btn').onclick = debounce(() => {
            console.log("觸發(fā)了")
        }, 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. 函數(shù)柯里化

function curry(fn,val){
  return function(){
    //轉(zhuǎn)化為數(shù)組
    var args = Array.from(arguments)
    if(val){
      //參數(shù)拼接
      args = args.concat(val)
    }
    //fn.length 表示函數(shù)需要傳入多少參數(shù)
    //遞歸調(diào)用 當(dāng)參數(shù)相等停止遞歸
    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就是調(diào)用reduce方法的數(shù)組
    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. 創(chuàng)建一個(gè)空對(duì)象 var obj = {}
  3. 修改obj.proto=Dog.prototype
  4. 只改this指向并且把參數(shù)傳遞過去,call和apply都可以
    根據(jù)規(guī)范,返回 null 和 undefined 不處理菊卷,依然返回obj
function _new(fn,...rest){
  //基于fn的prototype構(gòu)建對(duì)象的原型
  const thisObj = Object.create(fn.prototype);
  //將thisObj作為fn的this,繼承其屬性宝剖,并獲取返回結(jié)果為result
  const result = fn.apply(thisObj,rest);
  //根據(jù)result對(duì)象的類型決定返回結(jié)果
  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子元素是否為對(duì)象洁闰,如果是,遞歸復(fù)制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是万细,簡(jiǎn)單復(fù)制
                    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實(shí)現(xiàn)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子元素是否為對(duì)象扑眉,如果是,遞歸復(fù)制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是赖钞,簡(jiǎn)單復(fù)制
                    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(){
    //轉(zhuǎn)化為數(shù)組
    var args = Array.from(arguments)
    if(val){
      //參數(shù)拼接
      args = args.concat(val)
    }
    //fn.length 表示函數(shù)需要傳入多少參數(shù)
    //遞歸調(diào)用 當(dāng)參數(shù)相等停止遞歸
    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函數(shù)

        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()
        // 調(diào)用這個(gè) repeatFunc ("hellworld")腰素,會(huì)alert4次 helloworld, 每次間隔3秒

13. 手寫promise.all

function myPromiseAll(arr) {  // 參數(shù)是一個(gè)iterable對(duì)象,一般是數(shù)組
  // 返回一個(gè)Promise實(shí)例
   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);
               // 輸出傳入的數(shù)組中每一個(gè) promise 執(zhí)行后的狀態(tài)和值
               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;
    // 已完成的數(shù)量雪营,用于最終的返回弓千,不能直接用完成數(shù)量作為iteratorIndex
    // 輸出順序和完成順序是兩碼事
    let fullCount = 0;
    // 用于迭代iterator數(shù)據(jù)
    for (const item of args) {
      // for of 遍歷順序,用于返回正確順序的結(jié)果
      // 因iterator用forEach遍歷后的key和value一樣献起,所以必須存一份for of的 iteratorIndex
      let resultIndex = iteratorIndex;
      iteratorIndex += 1;
      // 包一層洋访,以兼容非promise的情況
      Promise.resolve(item).then(res => {
        promiseResults[resultIndex] = res;
        fullCount += 1;
        // Iterator 接口的數(shù)據(jù)無(wú)法單純的用length和size判斷長(zhǎng)度镣陕,不能局限于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. 實(shí)現(xiàn)一個(gè)eventbus

function EventBus() {}

EventBus.prototype.on = function (name, callback) {
 //如果沒有事件對(duì)象,新增一個(gè)
 if(!this._events){
  //創(chuàng)建一個(gè)干凈的沒有原型鏈的對(duì)象
  this._events = Object.create(null);
 }
 //如果沒有這個(gè)事件的訂閱姻政,新增一個(gè)呆抑,如果有,push進(jìn)去
 if(!this._events[name]){
  this._events[name] = [callback];
 }else{
  this._events[name].push(callback);
 }
}

EventBus.prototype.emit = function (name, ...args) {
 //發(fā)布的時(shí)候汁展,如果有這個(gè)事件鹊碍,循環(huán)執(zhí)行所有這個(gè)訂閱的方法
 if(this._events[name]){
  this._events[name].forEach(callback => {
   callback(...args);
  })
 }
}

EventBus.prototype.off = function (name) {
 //如果有這個(gè)事件的訂閱,清除所有訂閱
 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', '發(fā)布o(jì)n1')//發(fā)布o(jì)n1
eventBus.emit('on', '發(fā)布o(jì)n2')//發(fā)布o(jì)n2
eventBus.emit('once', '發(fā)布o(jì)nce')//發(fā)布o(jì)nce
eventBus.emit('once', '發(fā)布o(jì)nce')
eventBus.emit('off', '發(fā)布o(jì)ff')//發(fā)布o(jì)ff
eventBus.off('off')
eventBus.emit('off', '發(fā)布o(jì)ff')

16. 發(fā)布訂閱

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

    emit(type, ...args) {
        // 發(fā)布事件
        // 可以傳遞多個(gè)參數(shù)食绿,每個(gè)事件處理函數(shù)都會(huì)被執(zhí)行一次
        const listeners = this.events[type];
        for (const listener of listeners) {
            listener(...args);
        }
    }

    on(type, listener) {
        // 注冊(cè)事件
        // 一個(gè)事件可以綁定多個(gè)事件處理函數(shù)
        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. 數(shù)組扁平化

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也可以將數(shù)組轉(zhuǎn)換為字符串

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

4. 遞歸
遞歸的遍歷每一項(xiàng),若為數(shù)組則繼續(xù)遍歷炫欺,否則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. 擴(kuò)展運(yùn)算符
es6的擴(kuò)展運(yùn)算符能將二維數(shù)組變?yōu)橐痪S

[].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. 扁平數(shù)組轉(zhuǎn)換為數(shù)

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  // 這一塊可以寫一個(gè)深拷貝
      
      obj.children = getTree(temp, item.id)
      newArr = newArr.concat(obj)
    }
  })
  return newArr
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乎完,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子品洛,更是在濱河造成了極大的恐慌树姨,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桥状,死亡現(xiàn)場(chǎng)離奇詭異帽揪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辅斟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門转晰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人士飒,你說(shuō)我怎么就攤上這事查邢。” “怎么了酵幕?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵扰藕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我芳撒,道長(zhǎng)邓深,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任笔刹,我火速辦了婚禮芥备,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舌菜。我一直安慰自己萌壳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著讶凉,像睡著了一般染乌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上懂讯,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天荷憋,我揣著相機(jī)與錄音,去河邊找鬼褐望。 笑死勒庄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘫里。 我是一名探鬼主播实蔽,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谨读!你這毒婦竟也來(lái)了局装?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤劳殖,失蹤者是張志新(化名)和其女友劉穎铐尚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哆姻,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宣增,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矛缨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爹脾。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖箕昭,靈堂內(nèi)的尸體忽然破棺而出灵妨,到底是詐尸還是另有隱情,我是刑警寧澤落竹,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布闷串,位于F島的核電站,受9級(jí)特大地震影響筋量,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碉熄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一桨武、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锈津,春花似錦呀酸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窿吩。三九已至,卻和暖如春错览,著一層夾襖步出監(jiān)牢的瞬間纫雁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工倾哺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轧邪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓羞海,卻偏偏與公主長(zhǎng)得像忌愚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子却邓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 下面介紹一些常用的源碼實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)深拷貝實(shí)現(xiàn) new 操作符實(shí)現(xiàn)instanceof防抖節(jié)流函數(shù)柯里化實(shí)現(xiàn) cal...
    書蟲和泰迪熊閱讀 812評(píng)論 0 1
  • 前言 針對(duì)面試的 JavaScript 知識(shí)點(diǎn)整理 1.介紹一下js的數(shù)據(jù)類型有哪些硕糊,值是如何存儲(chǔ)的 JavaSc...
    Moon_f3e1閱讀 230評(píng)論 0 0
  • 目錄 閉包 類的繼承與創(chuàng)建[http://www.reibang.com/p/5ce78ff4bc9d] 如何解...
    Grandperhaps閱讀 1,149評(píng)論 2 30
  • 01 前言 2020是不平凡的一年,這一年里發(fā)生許多事情腊徙,大家也都知道简十。對(duì)于互聯(lián)網(wǎng)行業(yè)來(lái)說(shuō)也是一次重大的打擊,也有...
    前前前端小飛閱讀 39,587評(píng)論 5 70
  • 1昧穿、深拷貝deepCopy 2勺远、對(duì)象扁平化 3、數(shù)組扁平化 4时鸵、手寫Promise 5胶逢、promise.all方法...
    Adonia汪閱讀 650評(píng)論 1 12