日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
死磕36個(gè)JS手寫(xiě)題,搞懂后提升真的大

為什么要寫(xiě)這類文章

作為一個(gè)程序員,代碼能力毋庸置疑是非常非常重要的,就像現(xiàn)在為什么大廠面試基本都問(wèn)什么 API 怎么實(shí)現(xiàn)可見(jiàn)其重要性。我想說(shuō)的是居然手寫(xiě)這么重要,那我們就必須掌握它,所以文章標(biāo)題用了死磕,一點(diǎn)也不過(guò)分,也希望不被認(rèn)為是標(biāo)題黨。

成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括廣陵網(wǎng)站建設(shè)、廣陵網(wǎng)站制作、廣陵網(wǎng)頁(yè)制作以及廣陵網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,廣陵網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到廣陵省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

作為一個(gè)普通前端,我是真的寫(xiě)不出 Promise A+ 規(guī)范,但是沒(méi)關(guān)系,我們可以站在巨人的肩膀上,要相信我們現(xiàn)在要走的路,前人都走過(guò),所以可以找找現(xiàn)在社區(qū)已經(jīng)存在的那些優(yōu)秀的文章,比如工業(yè)聚大佬寫(xiě)的 100 行代碼實(shí)現(xiàn) Promises/A+ 規(guī)范,找到這些文章后不是收藏夾吃灰,得找個(gè)時(shí)間踏踏實(shí)實(shí)的學(xué),一行一行的磨,直到搞懂為止。我現(xiàn)在就是這么干的。

能收獲什么

這篇文章總體上分為 2 類手寫(xiě)題,前半部分可以歸納為是常見(jiàn)需求,后半部分則是對(duì)現(xiàn)有技術(shù)的實(shí)現(xiàn);

  • 對(duì)常用的需求進(jìn)行手寫(xiě)實(shí)現(xiàn),比如數(shù)據(jù)類型判斷函數(shù)、深拷貝等可以直接用于往后的項(xiàng)目中,提高了項(xiàng)目開(kāi)發(fā)效率;
  • 對(duì)現(xiàn)有關(guān)鍵字和 API 的實(shí)現(xiàn),可能需要用到別的知識(shí)或 API,比如在寫(xiě) forEach 的時(shí)候用到了無(wú)符號(hào)位右移的操作,平時(shí)都不怎么能夠接觸到這玩意,現(xiàn)在遇到了就可以順手把它掌握了。所以手寫(xiě)這些實(shí)現(xiàn)能夠潛移默化的擴(kuò)展并鞏固自己的 JS 基礎(chǔ);
  • 通過(guò)寫(xiě)各種測(cè)試用例,你會(huì)知道各種 API 的邊界情況,比如 Promise.all, 你得考慮到傳入?yún)?shù)的各種情況,從而加深了對(duì)它們的理解及使用;

閱讀的時(shí)候需要做什么

閱讀的時(shí)候,你需要把每行代碼都看懂,知道它在干什么,為什么要這么寫(xiě),能寫(xiě)得更好嘛?比如在寫(xiě)圖片懶加載的時(shí)候,一般我們都是根據(jù)當(dāng)前元素的位置和視口進(jìn)行判斷是否要加載這張圖片,普通程序員寫(xiě)到這就差不多完成了。而大佬程序員則是會(huì)多考慮一些細(xì)節(jié)的東西,比如性能如何更優(yōu)?代碼如何更精簡(jiǎn)?比如 yeyan1996 寫(xiě)的圖片懶加載就多考慮了 2 點(diǎn):比如圖片全部加載完成的時(shí)候得把事件監(jiān)聽(tīng)給移除;比如加載完一張圖片的時(shí)候,得把當(dāng)前 img 從 imgList 里移除,起到優(yōu)化內(nèi)存的作用。

除了讀通代碼之外,還可以打開(kāi) Chrome 的 Script snippet 去寫(xiě)測(cè)試用例跑跑代碼,做到更好的理解以及使用。

在看了幾篇以及寫(xiě)了很多測(cè)試用例的前提下,嘗試自己手寫(xiě)實(shí)現(xiàn),看看自己到底掌握了多少。條條大路通羅馬,你還能有別的方式實(shí)現(xiàn)嘛?或者你能寫(xiě)得比別人更好嘛?

好了,還楞著干啥,開(kāi)始干活。

數(shù)據(jù)類型判斷

typeof 可以正確識(shí)別:Undefined、Boolean、Number、String、Symbol、Function 等類型的數(shù)據(jù),但是對(duì)于其他的都會(huì)認(rèn)為是 object,比如 Null、Date 等,所以通過(guò) typeof 來(lái)判斷數(shù)據(jù)類型會(huì)不準(zhǔn)確。但是可以使用 Object.prototype.toString 實(shí)現(xiàn)。

 
 
 
 
  1. function typeOf(obj) { 
  2.     let res = Object.prototype.toString.call(obj).split(' ')[1] 
  3.     res = res.substring(0, res.length - 1).toLowerCase() 
  4.     return res 
  5. typeOf([])        // 'array' 
  6. typeOf({})        // 'object' 
  7. typeOf(new Date)  // 'date' 

繼承

原型鏈繼承

 
 
 
 
  1. function Animal() { 
  2.     this.colors = ['black', 'white'] 
  3. Animal.prototype.getColor = function() { 
  4.     return this.colors 
  5. function Dog() {} 
  6. Dog.prototype =  new Animal() 
  7.  
  8. let dog1 = new Dog() 
  9. dog1.colors.push('brown') 
  10. let dog2 = new Dog() 
  11. console.log(dog2.colors)  // ['black', 'white', 'brown'] 

原型鏈繼承存在的問(wèn)題:

  • 問(wèn)題1:原型中包含的引用類型屬性將被所有實(shí)例共享;
  • 問(wèn)題2:子類在實(shí)例化的時(shí)候不能給父類構(gòu)造函數(shù)傳參;

借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承

 
 
 
 
  1. function Animal(name) { 
  2.     this.name = name 
  3.     this.getName = function() { 
  4.         return this.name 
  5.     } 
  6. function Dog(name) { 
  7.     Animal.call(this, name) 
  8. Dog.prototype =  new Animal() 

借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承解決了原型鏈繼承的 2 個(gè)問(wèn)題:引用類型共享問(wèn)題以及傳參問(wèn)題。但是由于方法必須定義在構(gòu)造函數(shù)中,所以會(huì)導(dǎo)致每次創(chuàng)建子類實(shí)例都會(huì)創(chuàng)建一遍方法。

組合繼承

組合繼承結(jié)合了原型鏈和盜用構(gòu)造函數(shù),將兩者的優(yōu)點(diǎn)集中了起來(lái)?;镜乃悸肥鞘褂迷玩溊^承原型上的屬性和方法,而通過(guò)盜用構(gòu)造函數(shù)繼承實(shí)例屬性。這樣既可以把方法定義在原型上以實(shí)現(xiàn)重用,又可以讓每個(gè)實(shí)例都有自己的屬性。

 
 
 
 
  1. function Animal(name) { 
  2.     this.name = name 
  3.     this.colors = ['black', 'white'] 
  4. Animal.prototype.getName = function() { 
  5.     return this.name 
  6. function Dog(name, age) { 
  7.     Animal.call(this, name) 
  8.     this.age = age 
  9. Dog.prototype =  new Animal() 
  10. Dog.prototype.constructor = Dog 
  11.  
  12. let dog1 = new Dog('奶昔', 2) 
  13. dog1.colors.push('brown') 
  14. let dog2 = new Dog('哈赤', 1) 
  15. console.log(dog2)  
  16. // { name: "哈赤", colors: ["black", "white"], age: 1 } 

寄生式組合繼承

組合繼承已經(jīng)相對(duì)完善了,但還是存在問(wèn)題,它的問(wèn)題就是調(diào)用了 2 次父類構(gòu)造函數(shù),第一次是在 new Animal(),第二次是在 Animal.call() 這里。

所以解決方案就是不直接調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是通過(guò)創(chuàng)建空函數(shù) F 獲取父類原型的副本。

寄生式組合繼承寫(xiě)法上和組合繼承基本類似,區(qū)別是如下這里:

 
 
 
 
  1. - Dog.prototype =  new Animal() 
  2. - Dog.prototype.constructor = Dog 
  3.  
  4. + function F() {} 
  5. + F.prototype = Animal.prototype 
  6. + let f = new F() 
  7. + f.constructor = Dog 
  8. + Dog.prototype = f 

稍微封裝下上面添加的代碼后:

 
 
 
 
  1. function object(o) { 
  2.     function F() {} 
  3.     F.prototype = o 
  4.     return new F() 
  5. function inheritPrototype(child, parent) { 
  6.     let prototype = object(parent.prototype) 
  7.     prototype.constructor = child 
  8.     child.prototype = prototype 
  9. inheritPrototype(Dog, Animal) 

如果你嫌棄上面的代碼太多了,還可以基于組合繼承的代碼改成最簡(jiǎn)單的寄生式組合繼承:

 
 
 
 
  1. - Dog.prototype =  new Animal() 
  2. - Dog.prototype.constructor = Dog 
  3.  
  4. + Dog.prototype =  Object.create(Animal.prototype) 
  5. + Dog.prototype.constructor = Dog 

class 實(shí)現(xiàn)繼承

 
 
 
 
  1. class Animal { 
  2.     constructor(name) { 
  3.         this.name = name 
  4.     }  
  5.     getName() { 
  6.         return this.name 
  7.     } 
  8. class Dog extends Animal { 
  9.     constructor(name, age) { 
  10.         super(name) 
  11.         this.age = age 
  12.     } 

數(shù)組去重

ES5 實(shí)現(xiàn):

 
 
 
 
  1. function unique(arr) { 
  2.     var res = arr.filter(function(item, index, array) { 
  3.         return array.indexOf(item) === index 
  4.     }) 
  5.     return res 

ES6 實(shí)現(xiàn):

 
 
 
 
  1. var unique = arr => [...new Set(arr)] 

數(shù)組扁平化

數(shù)組扁平化就是將 [1, [2, [3]]] 這種多層的數(shù)組拍平成一層 [1, 2, 3]。使用 Array.prototype.flat 可以直接將多層數(shù)組拍平成一層:

 
 
 
 
  1. [1, [2, [3]]].flat(2)  // [1, 2, 3] 

現(xiàn)在就是要實(shí)現(xiàn) flat 這種效果。

ES5 實(shí)現(xiàn):遞歸。

 
 
 
 
  1. function flatten(arr) { 
  2.     var result = []; 
  3.     for (var i = 0, len = arr.length; i < len; i++) { 
  4.         if (Array.isArray(arr[i])) { 
  5.             result = result.concat(flatten(arr[i])) 
  6.         } else { 
  7.             result.push(arr[i]) 
  8.         } 
  9.     } 
  10.     return result; 

ES6 實(shí)現(xiàn):

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

深淺拷貝

淺拷貝:只考慮對(duì)象類型。

 
 
 
 
  1. function shallowCopy(obj) { 
  2.     if (typeof obj !== 'object') return 
  3.      
  4.     let newObj = obj instanceof Array ? [] : {} 
  5.     for (let key in obj) { 
  6.         if (obj.hasOwnProperty(key)) { 
  7.             newObj[key] = obj[key] 
  8.         } 
  9.     } 
  10.     return newObj 

簡(jiǎn)單版深拷貝:只考慮普通對(duì)象屬性,不考慮內(nèi)置對(duì)象和函數(shù)。

 
 
 
 
  1. function deepClone(obj) { 
  2.     if (typeof obj !== 'object') return; 
  3.     var newObj = obj instanceof Array ? [] : {}; 
  4.     for (var key in obj) { 
  5.         if (obj.hasOwnProperty(key)) { 
  6.             newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]; 
  7.         } 
  8.     } 
  9.     return newObj; 

復(fù)雜版深克?。夯诤?jiǎn)單版的基礎(chǔ)上,還考慮了內(nèi)置對(duì)象比如 Date、RegExp 等對(duì)象和函數(shù)以及解決了循環(huán)引用的問(wèn)題。

 
 
 
 
  1. const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null; 
  2.  
  3. function deepClone(target, map = new WeakMap()) { 
  4.     if (map.get(target)) { 
  5.         return target; 
  6.     } 
  7.     // 獲取當(dāng)前值的構(gòu)造函數(shù):獲取它的類型 
  8.     let constructor = target.constructor; 
  9.     // 檢測(cè)當(dāng)前對(duì)象target是否與正則、日期格式對(duì)象匹配 
  10.     if (/^(RegExp|Date)$/i.test(constructor.name)) { 
  11.         // 創(chuàng)建一個(gè)新的特殊對(duì)象(正則類/日期類)的實(shí)例 
  12.         return new constructor(target);   
  13.     } 
  14.     if (isObject(target)) { 
  15.         map.set(target, true);  // 為循環(huán)引用的對(duì)象做標(biāo)記 
  16.         const cloneTarget = Array.isArray(target) ? [] : {}; 
  17.         for (let prop in target) { 
  18.             if (target.hasOwnProperty(prop)) { 
  19.                 cloneTarget[prop] = deepClone(target[prop], map); 
  20.             } 
  21.         } 
  22.         return cloneTarget; 
  23.     } else { 
  24.         return target; 
  25.     } 

事件總線(發(fā)布訂閱模式)

 
 
 
 
  1. class EventEmitter { 
  2.     constructor() { 
  3.         this.cache = {} 
  4.     } 
  5.     on(name, fn) { 
  6.         if (this.cache[name]) { 
  7.             this.cache[name].push(fn) 
  8.         } else { 
  9.             this.cache[name] = [fn] 
  10.         } 
  11.     } 
  12.     off(name, fn) { 
  13.         let tasks = this.cache[name] 
  14.         if (tasks) { 
  15.             const index = tasks.findIndex(f => f === fn || f.callback === fn) 
  16.             if (index >= 0) { 
  17.                 tasks.splice(index, 1) 
  18.             } 
  19.         } 
  20.     } 
  21.     emit(name, once = false, ...args) { 
  22.         if (this.cache[name]) { 
  23.             // 創(chuàng)建副本,如果回調(diào)函數(shù)內(nèi)繼續(xù)注冊(cè)相同事件,會(huì)造成死循環(huán) 
  24.             let tasks = this.cache[name].slice() 
  25.             for (let fn of tasks) { 
  26.                 fn(...args) 
  27.             } 
  28.             if (once) { 
  29.                 delete this.cache[name] 
  30.             } 
  31.         } 
  32.     } 
  33.  
  34. // 測(cè)試 
  35. let eventBus = new EventEmitter() 
  36. let fn1 = function(name, age) { 
  37.     console.log(`${name} ${age}`) 
  38. let fn2 = function(name, age) { 
  39.     console.log(`hello, ${name} ${age}`) 
  40. eventBus.on('aaa', fn1) 
  41. eventBus.on('aaa', fn2) 
  42. eventBus.emit('aaa', false, '布蘭', 12) 
  43. // '布蘭 12' 
  44. // 'hello, 布蘭 12' 

解析 URL 參數(shù)為對(duì)象

 
 
 
 
  1. function parseParam(url) { 
  2.     const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 將 ? 后面的字符串取出來(lái) 
  3.     const paramsArr = paramsStr.split('&'); // 將字符串以 & 分割后存到數(shù)組中 
  4.     let paramsObj = {}; 
  5.     // 將 params 存到對(duì)象中 
  6.     paramsArr.forEach(param => { 
  7.         if (/=/.test(param)) { // 處理有 value 的參數(shù) 
  8.             let [key, val] = param.split('='); // 分割 key 和 value 
  9.             val = decodeURIComponent(val); // 解碼 
  10.             val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判斷是否轉(zhuǎn)為數(shù)字 
  11.      
  12.             if (paramsObj.hasOwnProperty(key)) { // 如果對(duì)象有 key,則添加一個(gè)值 
  13.                 paramsObj[key] = [].concat(paramsObj[key], val); 
  14.             } else { // 如果對(duì)象沒(méi)有這個(gè) key,創(chuàng)建 key 并設(shè)置值 
  15.                 paramsObj[key] = val; 
  16.             } 
  17.         } else { // 處理沒(méi)有 value 的參數(shù) 
  18.             paramsObj[param] = true; 
  19.         } 
  20.     }) 
  21.      
  22.     return paramsObj; 

字符串模板

 
 
 
 
  1. function render(template, data) { 
  2.     const reg = /\{\{(\w+)\}\}/; // 模板字符串正則 
  3.     if (reg.test(template)) { // 判斷模板里是否有模板字符串 
  4.         const name = reg.exec(template)[1]; // 查找當(dāng)前模板里第一個(gè)模板字符串的字段 
  5.         template = template.replace(reg, data[name]); // 將第一個(gè)模板字符串渲染 
  6.         return render(template, data); // 遞歸的渲染并返回渲染后的結(jié)構(gòu) 
  7.     } 
  8.     return template; // 如果模板沒(méi)有模板字符串直接返回 

測(cè)試:

 
 
 
 
  1. let template = '我是{{name}},年齡{{age}},性別{{sex}}'; 
  2. let person = { 
  3.     name: '布蘭', 
  4.     age: 12 
  5. render(template, person); // 我是布蘭,年齡12,性別undefined 

圖片懶加載

與普通的圖片懶加載不同,如下這個(gè)多做了 2 個(gè)精心處理:

  • 圖片全部加載完成后移除事件監(jiān)聽(tīng);
  • 加載完的圖片,從 imgList 移除;
 
 
 
 
  1. let imgList = [...document.querySelectorAll('img')] 
  2. let length = imgList.length 
  3.  
  4. const imgLazyLoad = function() { 
  5.     let count = 0 
  6.     return function() { 
  7.         let deleteIndexList = [] 
  8.         imgList.forEach((img, index) => { 
  9.             let rect = img.getBoundingClientRect() 
  10.             if (rect.top < window.innerHeight) { 
  11.                 img.src = img.dataset.src 
  12.                 deleteIndexList.push(index) 
  13.                 count++ 
  14.                 if (count === length) { 
  15.                     document.removeEventListener('scroll', imgLazyLoad) 
  16.                 } 
  17.             } 
  18.         }) 
  19.         imgList = imgList.filter((img, index) => !deleteIndexList.includes(index)) 
  20.     } 
  21.  
  22. // 這里最好加上防抖處理 
  23. document.addEventListener('scroll', imgLazyLoad) 

函數(shù)防抖

觸發(fā)高頻事件 N 秒后只會(huì)執(zhí)行一次,如果 N 秒內(nèi)事件再次觸發(fā),則會(huì)重新計(jì)時(shí)。

簡(jiǎn)單版:函數(shù)內(nèi)部支持使用 this 和 event 對(duì)象;

 
 
 
 
  1. function debounce(func, wait) { 
  2.     var timeout; 
  3.     return function () { 
  4.         var context = this; 
  5.         var args = arguments; 
  6.         clearTimeout(timeout) 
  7.         timeout = setTimeout(function(){ 
  8.             func.apply(context, args) 
  9.         }, wait); 
  10.     } 

 使用:

 
 
 
 
  1. var node = document.getElementById('layout') 
  2. function getUserAction(e) { 
  3.     console.log(this, e)  // 分別打印:node 這個(gè)節(jié)點(diǎn) 和 MouseEvent 
  4.     node.innerHTML = count++; 
  5. }; 
  6. node.onmousemove = debounce(getUserAction, 1000) 

最終版:除了支持 this 和 event 外,還支持以下功能:

  • 支持立即執(zhí)行;
  • 函數(shù)可能有返回值;
  • 支持取消功能;
 
 
 
 
  1. function debounce(func, wait, immediate) { 
  2.     var timeout, result; 
  3.      
  4.     var debounced = function () { 
  5.         var context = this; 
  6.         var args = arguments; 
  7.          
  8.         if (timeout) clearTimeout(timeout); 
  9.         if (immediate) { 
  10.             // 如果已經(jīng)執(zhí)行過(guò),不再執(zhí)行 
  11.             var callNow = !timeout; 
  12.             timeout = setTimeout(function(){ 
  13.                 timeout = null; 
  14.             }, wait) 
  15.             if (callNow) result = func.apply(context, args) 
  16.         } else { 
  17.             timeout = setTimeout(function(){ 
  18.                 func.apply(context, args) 
  19.             }, wait); 
  20.         } 
  21.         return result; 
  22.     }; 
  23.  
  24.     debounced.cancel = function() { 
  25.         clearTimeout(timeout); 
  26.         timeout = null; 
  27.     }; 
  28.  
  29.     return debounced; 

使用:

 
 
 
 
  1. var setUseAction = debounce(getUserAction, 10000, true); 
  2. // 使用防抖 
  3. node.onmousemove = setUseAction 
  4.  
  5. // 取消防抖 
  6. setUseAction.cancel() 

參考:JavaScript專題之跟著underscore學(xué)防抖

函數(shù)節(jié)流

觸發(fā)高頻事件,且 N 秒內(nèi)只執(zhí)行一次。

簡(jiǎn)單版:使用時(shí)間戳來(lái)實(shí)現(xiàn),立即執(zhí)行一次,然后每 N 秒執(zhí)行一次。

 
 
 
 
  1. function throttle(func, wait) { 
  2.     var context, args; 
  3.     var previous = 0; 
  4.  
  5.     return function() { 
  6.         var now = +new Date(); 
  7.         context = this; 
  8.         args = arguments; 
  9.         if (now - previous > wait) { 
  10.             func.apply(context, args); 
  11.             previous = now; 
  12.         } 
  13.     } 

最終版:支持取消節(jié)流;另外通過(guò)傳入第三個(gè)參數(shù),options.leading 來(lái)表示是否可以立即執(zhí)行一次,opitons.trailing 表示結(jié)束調(diào)用的時(shí)候是否還要執(zhí)行一次,默認(rèn)都是 true。注意設(shè)置的時(shí)候不能同時(shí)將 leading 或 trailing 設(shè)置為 false。

 
 
 
 
  1. function throttle(func, wait, options) { 
  2.     var timeout, context, args, result; 
  3.     var previous = 0; 
  4.     if (!options) options = {}; 
  5.  
  6.     var later = function() { 
  7.         previous = options.leading === false ? 0 : new Date().getTime(); 
  8.         timeout = null; 
  9.         func.apply(context, args); 
  10.         if (!timeout) context = args = null; 
  11.     }; 
  12.  
  13.     var throttled = function() { 
  14.         var now = new Date().getTime(); 
  15.         if (!previous && options.leading === false) previous = now; 
  16.         var remaining = wait - (now - previous); 
  17.         context = this; 
  18.         args = arguments; 
  19.         if (remaining <= 0 || remaining > wait) { 
  20.             if (timeout) { 
  21.                 clearTimeout(timeout); 
  22.                 timeout = null; 
  23.             } 
  24.             previous = now; 
  25.             func.apply(context, args); 
  26.             if (!timeout) context = args = null; 
  27.         } else if (!timeout && options.trailing !== false) { 
  28.             timeout = setTimeout(later, remaining); 
  29.         } 
  30.     }; 
  31.      
  32.     throttled.cancel = function() { 
  33.         clearTimeout(timeout); 
  34.         previous = 0; 
  35.         timeout = null; 
  36.     } 
  37.     return throttled; 

節(jié)流的使用就不拿代碼舉例了,參考防抖的寫(xiě)就行。

函數(shù)柯里化

什么叫函數(shù)柯里化?其實(shí)就是將使用多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換成一系列使用一個(gè)參數(shù)的函數(shù)的技術(shù)。還不懂?來(lái)舉個(gè)例子。

 
 
 
 
  1. function add(a, b, c) { 
  2.     return a + b + c 
  3. add(1, 2, 3) 
  4. let addCurry = curry(add) 
  5. addCurry(1)(2)(3) 

現(xiàn)在就是要實(shí)現(xiàn) curry 這個(gè)函數(shù),使函數(shù)從一次調(diào)用傳入多個(gè)參數(shù)變成多次調(diào)用每次傳一個(gè)參數(shù)。

 
 
 
 
  1. function curry(fn) { 
  2.     let judge = (...args) => { 
  3.         if (args.length == fn.length) return fn(...args) 
  4.         return (...arg) => judge(...args, ...arg) 
  5.     } 
  6.     return judge 

偏函數(shù)

什么是偏函數(shù)?偏函數(shù)就是將一個(gè) n 參的函數(shù)轉(zhuǎn)換成固定 x 參的函數(shù),剩余參數(shù)(n - x)將在下次調(diào)用全部傳入。舉個(gè)例子:

 
 
 
 
  1. function add(a, b, c) { 
  2.     return a + b + c 
  3. let partialAdd = partial(add, 1) 
  4. partialAdd(2, 3) 

發(fā)現(xiàn)沒(méi)有,其實(shí)偏函數(shù)和函數(shù)柯里化有點(diǎn)像,所以根據(jù)函數(shù)柯里化的實(shí)現(xiàn),能夠能很快寫(xiě)出偏函數(shù)的實(shí)現(xiàn):

 
 
 
 
  1. function partial(fn, ...args) { 
  2.     return (...arg) => { 
  3.         return fn(...args, ...arg) 
  4.     } 

如上這個(gè)功能比較簡(jiǎn)單,現(xiàn)在我們希望偏函數(shù)能和柯里化一樣能實(shí)現(xiàn)占位功能,比如:

 
 
 
 
  1. function clg(a, b, c) { 
  2.     console.log(a, b, c) 
  3. let partialClg = partial(clg, '_', 2) 
  4. partialClg(1, 3)  // 依次打?。?, 2, 3 

_ 占的位其實(shí)就是 1 的位置。相當(dāng)于:partial(clg, 1, 2),然后 partialClg(3)。明白了原理,我們就來(lái)寫(xiě)實(shí)現(xiàn):

 
 
 
 
  1. function partial(fn, ...args) { 
  2.     return (...arg) => { 
  3.         args[index] =  
  4.         return fn(...args, ...arg) 
  5.     } 

JSONP

JSONP 核心原理:script 標(biāo)簽不受同源策略約束,所以可以用來(lái)進(jìn)行跨域請(qǐng)求,優(yōu)點(diǎn)是兼容性好,但是只能用于 GET 請(qǐng)求;

 
 
 
 
  1. const jsonp = ({ url, params, callbackName }) => { 
  2.     const generateUrl = () => { 
  3.         let dataSrc = '' 
  4.         for (let key in params) { 
  5.             if (params.hasOwnProperty(key)) { 
  6.                 dataSrc += `${key}=${params[key]}&` 
  7.             } 
  8.         } 
  9.         dataSrc += `callback=${callbackName}` 
  10.         return `${url}?${dataSrc}` 
  11.     } 
  12.     return new Promise((resolve, reject) => { 
  13.         const scriptEle = document.createElement('script') 
  14.         scriptEle.src = generateUrl() 
  15.         document.body.appendChild(scriptEle) 
  16.         window[callbackName] = data => { 
  17.             resolve(data) 
  18.             document.removeChild(scriptEle) 
  19.         } 
  20.     }) 

AJAX

 
 
 
 
  1. const getJSON = function(url) { 
  2.     return new Promise((resolve, reject) => { 
  3.         const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp'); 
  4.         xhr.open('GET', url, false); 
  5.         xhr.setRequestHeader('Accept', 'application/json'); 
  6.         xhr.onreadystatechange = function() { 
  7.             if (xhr.readyState !== 4) return; 
  8.             if (xhr.status === 200 || xhr.status === 304) { 
  9.                 resolve(xhr.responseText); 
  10.             } else { 
  11.                 reject(new Error(xhr.responseText)); 
  12.             } 
  13.         } 
  14.         xhr.send(); 
  15.     }) 

實(shí)現(xiàn)數(shù)組原型方法

forEach

 
 
 
 
  1. Array.prototype.forEach2 = function(callback, thisArg) { 
  2.     if (this == null) { 
  3.         throw new TypeError('this is null or not defined') 
  4.     } 
  5.     if (typeof callback !== "function") { 
  6.         throw new TypeError(callback + ' is not a function') 
  7.     } 
  8.     const O = Object(this)  // this 就是當(dāng)前的數(shù)組 
  9.     const len = O.length >>> 0  // 后面有解釋 
  10.     let k = 0 
  11.     while (k < len) { 
  12.         if (k in O) { 
  13.             callback.call(thisArg, O[k], k, O); 
  14.         } 
  15.         k++; 
  16.     } 

O.length >>> 0 是什么操作?就是無(wú)符號(hào)右移 0 位,那有什么意義嘛?就是為了保證轉(zhuǎn)換后的值為正整數(shù)。其實(shí)底層做了 2 層轉(zhuǎn)換,第一是非 number 轉(zhuǎn)成 number 類型,第二是將 number 轉(zhuǎn)成 Uint32 類型。感興趣可以閱讀 something >>> 0是什么意思?[3]。

map

基于 forEach 的實(shí)現(xiàn)能夠很容易寫(xiě)出 map 的實(shí)現(xiàn):

 
 
 
 
  1. - Array.prototype.forEach2 = function(callback,
    網(wǎng)頁(yè)標(biāo)題:死磕36個(gè)JS手寫(xiě)題,搞懂后提升真的大
    URL鏈接:http://www.5511xx.com/article/cdjcjog.html