自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

【前端】一網(wǎng)打盡──前端進階和面試必會的8個手寫代碼

開發(fā) 前端
我們知道在前端進階和面試的時候,會考察到很多手寫源碼的問題,這些是通過學習和練習是可以掌握的,下面列舉了八個手寫的代碼系列,希望能夠對你有所幫助。

[[401822]]

寫在前面

我們知道在前端進階和面試的時候,會考察到很多手寫源碼的問題,這些是通過學習和練習是可以掌握的,下面列舉了八個手寫的代碼系列,希望能夠對你有所幫助。

1 手寫Promise系列

在Promise的學習中,之前也寫過相關的分享文章,敬請參見《從小白視角上手Promise、Async/Await和手撕代碼》。

1.1 Promise.all

  1. //手寫promise.all 
  2. Promise.prototype._all = promiseList => { 
  3.   // 當輸入的是一個promise列表 
  4.   const len = promiseList.length; 
  5.   const result = []; 
  6.   let count = 0; 
  7.   //  
  8.   return new Promise((resolve,reject)=>{ 
  9.     // 循環(huán)遍歷promise列表中的promise事件 
  10.     for(let i = 0; i < len; i++){ 
  11.       // 遍歷到第i個promise事件,判斷其事件是成功還是失敗 
  12.       promiseList[i].then(data=>{ 
  13.         result[i] = data; 
  14.         count++; 
  15.         // 當遍歷到最后一個promise時,結果的數(shù)組長度和promise列表長度一致,說明成功 
  16.         count === len && resolve(result); 
  17.       },error=>{ 
  18.         return reject(error); 
  19.       }) 
  20.     } 
  21.   }) 

1.2 Promise.race

  1. // 手寫promise.race 
  2. Promise.prototype._race = promiseList => { 
  3.   const len = promiseList.length; 
  4.   return new Promise((resolve,reject)=>{ 
  5.     // 循環(huán)遍歷promise列表中的promise事件 
  6.     for(let i = 0; i < len; i++){ 
  7.       promiseList[i]().then(data=>{ 
  8.         return resolve(data); 
  9.       },error=>{ 
  10.         return reject(error); 
  11.       }) 
  12.     } 
  13.   }) 

1.3 Promise.finally

  1. Promise.prototype._finally = function(promiseFunc){ 
  2.   return this.then(data=>Promise.resolve(promiseFunc()).then(data=>data) 
  3.   ,error=>Promise.reject(promiseFunc()).then(error=>{throw error})) 

2 手寫Aysnc/Await

  1. function asyncGenertor(genFunc){ 
  2.   return new Promise((resolve,reject)=>{ 
  3.     // 生成一個迭代器 
  4.     const gen = genFunc(); 
  5.     const step = (type,args)=>{ 
  6.       let next
  7.       try{ 
  8.         next = gen[type](args); 
  9.       }catch(e){ 
  10.         return reject(e); 
  11.       } 
  12.       // 從next中獲取done和value的值 
  13.       const {done,value} = next
  14.       // 如果迭代器的狀態(tài)是true 
  15.       if(done) return resolve(value); 
  16.       Promise.resolve(value).then
  17.         val=>step("next",val), 
  18.         err=>step("throw",err) 
  19.       ) 
  20.     } 
  21.     step("next"); 
  22.   }) 

3 深拷貝

深拷貝:拷貝所有的屬性值,以及屬性地址指向的值的內存空間。

3.1 丟失引用的深拷貝

當遇到對象時,就再新開一個對象,然后將第二層源對象的屬性值,完整地拷貝到這個新開的對象中。

  1. // 丟失引用的深拷貝 
  2. function deepClone(obj){ 
  3.   // 判斷obj的類型是否為object類型 
  4.   if(!obj && typeof obj !== "object"return
  5.   // 判斷對象是數(shù)組類型還是對象類型 
  6.   let newObj = Array.isArray(obj) ? [] : {}; 
  7.   // 遍歷obj的鍵值對 
  8.   for(const [key,value] of Object.entries(obj)){ 
  9.     newObj[key] = typeof value === "string" ? deepClone(value) : value; 
  10.   }; 
  11.   return newObj; 

3.2 終極方案的深拷貝(棧和深度優(yōu)先的思想)

其思路是:引入一個數(shù)組 uniqueList 用來存儲已經(jīng)拷貝的數(shù)組,每次循環(huán)遍歷時,先判斷對象是否在 uniqueList 中了,如果在的話就不執(zhí)行拷貝邏輯了。

  1. function deepCopy(obj){ 
  2.   // 用于去重 
  3.   const uniqueList = []; 
  4.   // 設置根節(jié)點 
  5.   let root = {}; 
  6.   // 遍歷數(shù)組 
  7.   const loopList = [{ 
  8.     parent: root, 
  9.     key: undefined, 
  10.     data: obj 
  11.   }]; 
  12.   // 遍歷循環(huán) 
  13.   while(loopList.length){ 
  14.     // 深度優(yōu)先-將數(shù)組最后的元素取出 
  15.     const {parent,key,data} = loopList.pop(); 
  16.     // 初始化賦值目標,key--undefined時拷貝到父元素,否則拷貝到子元素 
  17.     let result = parent; 
  18.     if(typeof key !== "undefined") result = parent[key] = {}; 
  19.     // 數(shù)據(jù)已存在時 
  20.     let uniqueData = uniqueList.find(item=>item.source === data); 
  21.     if(uniqueData){ 
  22.       parent[key] = uniqueData.target; 
  23.       // 中斷本次循環(huán) 
  24.       continue
  25.     } 
  26.     // 數(shù)據(jù)不存在時 
  27.     // 保存源數(shù)據(jù),在拷貝數(shù)據(jù)中對應的引用 
  28.     uniqueList.push({ 
  29.       source:data, 
  30.       target:result 
  31.     }); 
  32.     // 遍歷數(shù)據(jù) 
  33.     for(let k in data){ 
  34.       if(data.hasOwnProperty(k)){ 
  35.         typeof data[k] === "object"  
  36.         ? 
  37.           // 下一次循環(huán) 
  38.           loopList.push({ 
  39.             parent:result, 
  40.             key:k, 
  41.             data:data[k] 
  42.           }) 
  43.         :  
  44.         result[k] = data[k]; 
  45.          
  46.       } 
  47.     } 
  48.   } 
  49.   return root; 

4 手寫一個單例模式

單例模式:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。實現(xiàn)方法一般是先判斷實例是否存在,如果存在直接返回,如果不存在就先創(chuàng)建再返回。

  1. // 創(chuàng)建單例對象,使用閉包 
  2. const getSingle = function(func){ 
  3.   let result; 
  4.   return function(){ 
  5.     return result || (result = func.apply(this,arguments)); 
  6.   } 
  7.  
  8. // 使用Proxy攔截 
  9. const proxy = function(func){ 
  10.   let reuslt; 
  11.   const handler = { 
  12.     construct:function(){ 
  13.       if(!result) result = Reflect.construct(func,arguments); 
  14.       return result; 
  15.     } 
  16.   } 
  17.   return new Proxy(func,hendler); 

5 手寫封裝一個ajax函數(shù)

  1. /*  
  2. 封裝自己的ajax函數(shù) 
  3. 參數(shù)1:{string} method 請求方法 
  4. 參數(shù)2:{string} url 請求地址 
  5. 參數(shù)2:{Object} params 請求參數(shù) 
  6. 參數(shù)3:{function} done 請求完成后執(zhí)行的回調函數(shù) 
  7. */ 
  8.  
  9. function ajax(method,url,params,done){ 
  10.   // 1.創(chuàng)建xhr對象,兼容寫法 
  11.   let xhr = window.XMLHttpRequest  
  12.   ? new XMLHttpRequest() 
  13.   : new ActiveXObject("Microsoft.XMLHTTP"); 
  14.  
  15.   // 將method轉換成大寫 
  16.   method = method.toUpperCase(); 
  17.   // 參數(shù)拼接 
  18.   let newParams = []; 
  19.   for(let key in params){ 
  20.     newParams.push(`${key}=${params[k]}`); 
  21.   } 
  22.   let str = newParams.join("&"); 
  23.   // 判斷請求方法 
  24.   if(method === "GET") url += `?${str}`; 
  25.  
  26.   // 打開請求方式 
  27.   xhr.open(method,url); 
  28.  
  29.   let data = null
  30.   if(method === "POST"){ 
  31.     // 設置請求頭 
  32.     xhr.setRequestHeader(("Content-Type","application/x-www-form-urlencoded")); 
  33.     data = str; 
  34.   } 
  35.   xhr.send(data); 
  36.  
  37.   // 指定xhr狀態(tài)變化事件處理函數(shù) 
  38.   // 執(zhí)行回調函數(shù) 
  39.   xhr.onreadystatechange = function(){ 
  40.     if(this.readyState === 4) done(JSON.parse(xhr.responseText)); 
  41.   } 

6 手寫“防抖”和“節(jié)流”

在Promise的學習中,之前也寫過相關的分享文章,敬請參見《一網(wǎng)打盡──他們都在用這些”防抖“和”節(jié)流“方法》。

6.1 防抖

  1. /*  
  2. func:要進行防抖處理的函數(shù) 
  3. delay:要進行延時的時間 
  4. immediate:是否使用立即執(zhí)行 true立即執(zhí)行 false非立即執(zhí)行 
  5. */ 
  6. function debounce(func,delay,immediate){ 
  7.   let timeout; //定時器 
  8.   return function(arguments){ 
  9.     // 判斷定時器是否存在,存在的話進行清除,重新進行定時器計數(shù) 
  10.     if(timeout) clearTimeout(timeout); 
  11.     // 判斷是立即執(zhí)行的防抖還是非立即執(zhí)行的防抖 
  12.     if(immediate){//立即執(zhí)行 
  13.       const flag = !timeout;//此處是取反操作 
  14.       timeout = setTimeout(()=>{ 
  15.         timeout = null
  16.       },delay); 
  17.       // 觸發(fā)事件后函數(shù)會立即執(zhí)行,然后 n 秒內不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果。 
  18.       if(flag) func.call(this,arguments); 
  19.     }else{//非立即執(zhí)行 
  20.       timeout = setTimeout(()=>{ 
  21.         func.call(this,arguments); 
  22.       },delay) 
  23.     } 
  24.  
  25.   } 

6.2 節(jié)流

  1. // 節(jié)流--定時器版 
  2.  function throttle(func,delay){ 
  3.    let timeout;//定義一個定時器標記 
  4.    return function(arguments){ 
  5.      // 判斷是否存在定時器 
  6.      if(!timeout){  
  7.        // 創(chuàng)建一個定時器 
  8.        timeout = setTimeout(()=>{ 
  9.          // delay時間間隔清空定時器 
  10.          clearTimeout(timeout); 
  11.          func.call(this,arguments); 
  12.        },delay) 
  13.      } 
  14.    } 
  15.  } 

7 手寫apply、bind、call

7.1 apply

傳遞給函數(shù)的參數(shù)處理,不太一樣,其他部分跟call一樣。

apply接受第二個參數(shù)為類數(shù)組對象, 這里用了《JavaScript權威指南》中判斷是否為類數(shù)組對象的方法。

  1. Function.prototype._apply = function (context) { 
  2.     if (context === null || context === undefined) { 
  3.         context = window // 指定為 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中為window) 
  4.     } else { 
  5.         context = Object(context) // 值為原始值(數(shù)字,字符串,布爾值)的 this 會指向該原始值的實例對象 
  6.     } 
  7.     // JavaScript權威指南判斷是否為類數(shù)組對象 
  8.     function isArrayLike(o) { 
  9.         if (o &&                                    // o不是null、undefined等 
  10.             typeof o === 'object' &&                // o是對象 
  11.             isFinite(o.length) &&                   // o.length是有限數(shù)值 
  12.             o.length >= 0 &&                        // o.length為非負值 
  13.             o.length === Math.floor(o.length) &&    // o.length是整數(shù) 
  14.             o.length < 4294967296)                  // o.length < 2^32 
  15.             return true 
  16.         else 
  17.             return false 
  18.     } 
  19.     const specialPrototype = Symbol('特殊屬性Symbol') // 用于臨時儲存函數(shù) 
  20.     context[specialPrototype] = this; // 隱式綁定this指向到context上 
  21.     let args = arguments[1]; // 獲取參數(shù)數(shù)組 
  22.     let result 
  23.     // 處理傳進來的第二個參數(shù) 
  24.     if (args) { 
  25.         // 是否傳遞第二個參數(shù) 
  26.         if (!Array.isArray(args) && !isArrayLike(args)) { 
  27.             throw new TypeError('myApply 第二個參數(shù)不為數(shù)組并且不為類數(shù)組對象拋出錯誤'); 
  28.         } else { 
  29.             args = Array.from(args) // 轉為數(shù)組 
  30.             result = context[specialPrototype](...args); // 執(zhí)行函數(shù)并展開數(shù)組,傳遞函數(shù)參數(shù) 
  31.         } 
  32.     } else { 
  33.         result = context[specialPrototype](); // 執(zhí)行函數(shù)  
  34.     } 
  35.     delete context[specialPrototype]; // 刪除上下文對象的屬性 
  36.     return result; // 返回函數(shù)執(zhí)行結果 
  37. }; 

7.2 bind

拷貝源函數(shù):

  • 通過變量儲存源函數(shù)
  • 使用Object.create復制源函數(shù)的prototype給fToBind

返回拷貝的函數(shù)

調用拷貝的函數(shù):

  • new調用判斷:通過instanceof判斷函數(shù)是否通過new調用,來決定綁定的context
  • 綁定this+傳遞參數(shù)
  • 返回源函數(shù)的執(zhí)行結果
  1. Function.prototype._bind = function (objThis, ...params) { 
  2.     const thisFn = this; // 存儲源函數(shù)以及上方的params(函數(shù)參數(shù)) 
  3.     // 對返回的函數(shù) secondParams 二次傳參 
  4.     let fToBind = function (...secondParams) { 
  5.         const isNew = this instanceof fToBind // this是否是fToBind的實例 也就是返回的fToBind是否通過new調用 
  6.         const context = isNew ? this : Object(objThis) // new調用就綁定到this上,否則就綁定到傳入的objThis上 
  7.         return thisFn.call(context, ...params, ...secondParams); // 用call調用源函數(shù)綁定this的指向并傳遞參數(shù),返回執(zhí)行結果 
  8.     }; 
  9.     if (thisFn.prototype) { 
  10.         // 復制源函數(shù)的prototype給fToBind 一些情況下函數(shù)沒有prototype,比如箭頭函數(shù) 
  11.         fToBind.prototype = Object.create(thisFn.prototype); 
  12.     } 
  13.     return fToBind; // 返回拷貝的函數(shù) 
  14. }; 

7.3 call

根據(jù)call的規(guī)則設置上下文對象,也就是this的指向。

通過設置context的屬性,將函數(shù)的this指向隱式綁定到context上

通過隱式綁定執(zhí)行函數(shù)并傳遞參數(shù)。

刪除臨時屬性,返回函數(shù)執(zhí)行結果

  1. Function.prototype._call = function (context, ...arr) { 
  2.     if (context === null || context === undefined) { 
  3.        // 指定為 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中為window) 
  4.         context = window  
  5.     } else { 
  6.         context = Object(context) // 值為原始值(數(shù)字,字符串,布爾值)的 this 會指向該原始值的實例對象 
  7.     } 
  8.     const specialPrototype = Symbol('特殊屬性Symbol') // 用于臨時儲存函數(shù) 
  9.     context[specialPrototype] = this; // 函數(shù)的this指向隱式綁定到context上 
  10.     let result = context[specialPrototype](...arr); // 通過隱式綁定執(zhí)行函數(shù)并傳遞參數(shù) 
  11.     delete context[specialPrototype]; // 刪除上下文對象的屬性 
  12.     return result; // 返回函數(shù)執(zhí)行結果 
  13. }; 

8 手寫繼承

8.1 構造函數(shù)式繼承

構造函數(shù)式繼承并沒有繼承父類原型上的方法。

  1. function fatherUser(username, password) { 
  2.   let _password = password  
  3.   this.username = username  
  4.   fatherUser.prototype.login = function () { 
  5.       console.log(this.username + '要登錄父親賬號,密碼是' + _password) 
  6.   } 
  7.  
  8. function sonUser(username, password) { 
  9.   fatherUser.call(this, username, password
  10.   this.articles = 3 // 文章數(shù)量 
  11.  
  12. const yichuanUser = new sonUser('yichuan''xxx'
  13. console.log(yichuanUser.username) // yichuan 
  14. console.log(yichuanUser.username) // xxx 
  15. console.log(yichuanUser.login()) // TypeError: yichuanUser.login is not a function 

8.2 組合式繼承

  1. function fatherUser(username, password) { 
  2.   let _password = password  
  3.   this.username = username  
  4.   fatherUser.prototype.login = function () { 
  5.       console.log(this.username + '要登錄fatherUser,密碼是' + _password) 
  6.   } 
  7.  
  8. function sonUser(username, password) { 
  9.   fatherUser.call(this, username, password) // 第二次執(zhí)行 fatherUser 的構造函數(shù) 
  10.   this.articles = 3 // 文章數(shù)量 
  11.  
  12. sonUser.prototype = new fatherUser(); // 第二次執(zhí)行 fatherUser 的構造函數(shù) 
  13. const yichuanUser = new sonUser('yichuan''xxx'

8.3 寄生組合繼承

上面的繼承方式有所缺陷,所以寫這種方式即可。

  1. function Parent() { 
  2.   this.name = 'parent'
  3. function Child() { 
  4.   Parent.call(this); 
  5.   this.type = 'children'
  6. Child.prototype = Object.create(Parent.prototype); 
  7. Child.prototype.constructor = Child; 

參考文章

  • 《前端進階之必會的JavaScript技巧總結》
  • 《js基礎-面試官想知道你有多理解call,apply,bind?[不看后悔系列]》

 

責任編輯:姜華 來源: 前端萬有引力
相關推薦

2019-12-13 16:00:11

Dubbo面試題Java

2024-06-12 00:00:05

2024-04-26 00:25:52

Rust語法生命周期

2024-02-27 10:11:36

前端CSS@規(guī)則

2021-08-05 06:54:05

流程控制default

2024-06-24 12:51:50

2021-10-11 07:55:42

瀏覽器語法Webpack

2013-08-02 10:52:10

Android UI控件

2024-04-07 08:41:34

2024-08-26 10:01:50

2023-09-06 18:37:45

CSS選擇器符號

2010-08-25 01:59:00

2011-12-02 09:22:23

網(wǎng)絡管理NetQos

2015-06-01 10:37:41

數(shù)字取證數(shù)字取證工具

2019-07-24 15:30:00

SQL注入數(shù)據(jù)庫

2020-02-21 08:45:45

PythonWeb開發(fā)框架

2013-10-16 14:18:02

工具圖像處理

2023-04-06 09:08:41

BPM流程引擎

2019-12-03 10:45:35

NodeMySQLGit

2021-05-20 11:17:49

加密貨幣區(qū)塊鏈印度
點贊
收藏

51CTO技術棧公眾號