<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文幫你搞定90%的JS手寫題!面試手寫題不慌了

          共 44190字,需瀏覽 89分鐘

           ·

          2021-06-24 08:14

          點(diǎn)擊上方 程序員成長指北,關(guān)注公眾號

          回復(fù)1,加入高級Node交流群


          還在害怕手寫題嗎,本文可以幫你擴(kuò)展并鞏固自己的JS基礎(chǔ),順便搞定90%的手寫題。在工作中還可以對常用的需求進(jìn)行手寫實(shí)現(xiàn),比如深拷貝、防抖節(jié)流等可以直接用于往后的項(xiàng)目中,提高項(xiàng)目開發(fā)效率。不說廢話了,下面就直接上代碼吧。

          1.call的實(shí)現(xiàn)

          • 第一個(gè)參數(shù)為null或者undefined時(shí),this指向全局對象window,值為原始值的指向該原始值的自動包裝對象,如 String、Number、Boolean
          • 為了避免函數(shù)名與上下文(context)的屬性發(fā)生沖突,使用Symbol類型作為唯一值
          • 將函數(shù)作為傳入的上下文(context)屬性執(zhí)行
          • 函數(shù)執(zhí)行完成后刪除該屬性
          • 返回執(zhí)行結(jié)果
          Function.prototype.myCall = function(context,...args){
              let cxt = context || window;
              //將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對象調(diào)用形式綁定this)
              //新建一個(gè)唯一的Symbol變量避免重復(fù)
              let func = Symbol() 
              cxt[func] = this;
              args = args ? args : []
              //以對象調(diào)用形式調(diào)用func,此時(shí)this指向cxt 也就是傳入的需要綁定的this指向
              const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
              //刪除該方法,不然會對傳入對象造成污染(添加該方法)
              delete cxt[func];
              return res;
          }

          2.apply的實(shí)現(xiàn)

          • 前部分與call一樣
          • 第二個(gè)參數(shù)可以不傳,但類型必須為數(shù)組或者類數(shù)組
          Function.prototype.myApply = function(context,args = []){
              let cxt = context || window;
              //將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對象調(diào)用形式綁定this)
              //新建一個(gè)唯一的Symbol變量避免重復(fù)
              let func = Symbol()
              cxt[func] = this;
              //以對象調(diào)用形式調(diào)用func,此時(shí)this指向cxt 也就是傳入的需要綁定的this指向
              const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
              delete cxt[func];
              return res;
          }

          3.bind的實(shí)現(xiàn)

          需要考慮:

          • bind() 除了 this 外,還可傳入多個(gè)參數(shù);
          • bind 創(chuàng)建的新函數(shù)可能傳入多個(gè)參數(shù);
          • 新函數(shù)可能被當(dāng)做構(gòu)造函數(shù)調(diào)用;
          • 函數(shù)可能有返回值;

          實(shí)現(xiàn)方法:

          • bind 方法不會立即執(zhí)行,需要返回一個(gè)待執(zhí)行的函數(shù);(閉包)
          • 實(shí)現(xiàn)作用域綁定(apply)
          • 參數(shù)傳遞(apply 的數(shù)組傳參)
          • 當(dāng)作為構(gòu)造函數(shù)的時(shí)候,進(jìn)行原型繼承
          Function.prototype.myBind = function (context, ...args) {
              //新建一個(gè)變量賦值為this,表示當(dāng)前函數(shù)
              const fn = this
              //判斷有沒有傳參進(jìn)來,若為空則賦值[]
              args = args ? args : []
              //返回一個(gè)newFn函數(shù),在里面調(diào)用fn
              return function newFn(...newFnArgs) {
                  if (this instanceof newFn) {
                      return new fn(...args, ...newFnArgs)
                  }
                  return fn.apply(context, [...args,...newFnArgs])
              }
          }
          • 測試
          let name = '小王',age =17;
          let obj = {
              name:'小張',
              age: this.age,
              myFun: function(from,to){
                  console.log(this.name + ' 年齡 ' + this.age+'來自 '+from+'去往'+ to)
              }
          }
          let db = {
              name: '德瑪',
              age: 99
          }

          //結(jié)果
          obj.myFun.myCall(db,'成都','上海');     // 德瑪 年齡 99  來自 成都去往上海
          obj.myFun.myApply(db,['成都','上海']);      // 德瑪 年齡 99  來自 成都去往上海
          obj.myFun.myBind(db,'成都','上海')();       // 德瑪 年齡 99  來自 成都去往上海
          obj.myFun.myBind(db,['成都','上海'])();   // 德瑪 年齡 99  來自 成都, 上海去往 undefined

          4.寄生式組合繼承

          function Person(obj) {
              this.name = obj.name
              this.age = obj.age
          }
          Person.prototype.add = function(value){
              console.log(value)
          }
          var p1 = new Person({name:"番茄", age: 18})

          function Person1(obj) {
              Person.call(this, obj)
              this.sex = obj.sex
          }
          // 這一步是繼承的關(guān)鍵
          Person1.prototype = Object.create(Person.prototype);
          Person1.prototype.constructor = Person1;

          Person1.prototype.play = function(value){
              console.log(value)
          }
          var p2 = new Person1({name:"雞蛋", age: 118, sex: "男"})

          5.ES6繼承

          //class 相當(dāng)于es5中構(gòu)造函數(shù)
          //class中定義方法時(shí),前后不能加function,全部定義在class的protopyte屬性中
          //class中定義的所有方法是不可枚舉的
          //class中只能定義方法,不能定義對象,變量等
          //class和方法內(nèi)默認(rèn)都是嚴(yán)格模式
          //es5中constructor為隱式屬性
          class People{
            constructor(name='wang',age='27'){
              this.name = name;
              this.age = age;
            }
            eat(){
              console.log(`${this.name} ${this.age} eat food`)
            }
          }
          //繼承父類
          class Woman extends People
             constructor(name = 'ren',age = '27'){ 
               //繼承父類屬性
               super(name, age); 
             } 
              eat(){ 
               //繼承父類方法
                super.eat() 
              } 

          let wonmanObj=new Woman('xiaoxiami'); 
          wonmanObj.eat();

          //es5繼承先創(chuàng)建子類的實(shí)例對象,然后再將父類的方法添加到this上(Parent.apply(this))。 
          //es6繼承是使用關(guān)鍵字super先創(chuàng)建父類的實(shí)例對象this,最后在子類class中修改this。

          6.new的實(shí)現(xiàn)

          • 一個(gè)繼承自 Foo.prototype 的新對象被創(chuàng)建。
          • 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo,并將 this 綁定到新創(chuàng)建的對象。new Foo 等同于 new Foo(),也就是沒有指定參數(shù)列表,F(xiàn)oo 不帶任何參數(shù)調(diào)用的情況。
          • 由構(gòu)造函數(shù)返回的對象就是 new 表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個(gè)對象,則使用步驟1創(chuàng)建的對象。
          • 一般情況下,構(gòu)造函數(shù)不返回值,但是用戶可以選擇主動返回對象,來覆蓋正常的對象創(chuàng)建步驟
          function Ctor(){
              ....
          }

          function myNew(ctor,...args){
              if(typeof ctor !== 'function'){
                throw 'myNew function the first param must be a function';
              }
              var newObj = Object.create(ctor.prototype); //創(chuàng)建一個(gè)繼承自ctor.prototype的新對象
              var ctorReturnResult = ctor.apply(newObj, args); //將構(gòu)造函數(shù)ctor的this綁定到newObj中
              var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
              var isFunction = typeof ctorReturnResult === 'function';
              if(isObject || isFunction){
                  return ctorReturnResult;
              }
              return newObj;
          }

          let c = myNew(Ctor);

          7.instanceof的實(shí)現(xiàn)

          • instanceof 是用來判斷A是否為B的實(shí)例,表達(dá)式為:A instanceof B,如果A是B的實(shí)例,則返回true,否則返回false。
          • instanceof 運(yùn)算符用來測試一個(gè)對象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性。
          • 不能檢測基本數(shù)據(jù)類型,在原型鏈上的結(jié)果未必準(zhǔn)確,不能檢測null,undefined
          • 實(shí)現(xiàn):遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果沒有找到,返回 false
          function myInstanceOf(a,b){
              let left = a.__proto__;
              let right = b.prototype;
              while(true){
                  if(left == null){
                      return false
                  }
                  if(left == right){
                      return true
                  }
                  left = left.__proto__
              }
          }

          //instanceof 運(yùn)算符用于判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對象的原型鏈中的任何位置。
          function myInstanceof(left, right) {
              let proto = Object.getPrototypeOf(left), // 獲取對象的原型
              prototype = right.prototype; // 獲取構(gòu)造函數(shù)的 prototype 對象
              // 判斷構(gòu)造函數(shù)的 prototype 對象是否在對象的原型鏈上
              while (true) {
                  if (!proto) return false;
                  if (proto === prototype) return true;
                  proto = Object.getPrototypeOf(proto);
              }
          }

          8.Object.create()的實(shí)現(xiàn)

          • MDN文檔
          • Object.create()會將參數(shù)對象作為一個(gè)新創(chuàng)建的空對象的原型, 并返回這個(gè)空對象
          //簡略版
          function myCreate(obj){
              // 新聲明一個(gè)函數(shù)
              function C(){};
              // 將函數(shù)的原型指向obj
              C.prototype = obj;
              // 返回這個(gè)函數(shù)的實(shí)力化對象
              return new C()
          }
          //官方版Polyfill
          if (typeof Object.create !== "function") {
              Object.create = function (proto, propertiesObject) {
                  if (typeof proto !== 'object' && typeof proto !== 'function') {
                      throw new TypeError('Object prototype may only be an Object: ' + proto);
                  } else if (proto === null) {
                      throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
                  }

                  if (typeof propertiesObject !== 'undefined'throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");

                  function F() {}
                  F.prototype = proto;

                  return new F();
              };
          }

          9.實(shí)現(xiàn) Object.assign

          Object.assign2 = function(target, ...source) {
              if (target == null) {
                  throw new TypeError('Cannot convert undefined or null to object')
              }
              let ret = Object(target) 
              source.forEach(function(obj) {
                  if (obj != null) {
                      for (let key in obj) {
                          if (obj.hasOwnProperty(key)) {
                              ret[key] = obj[key]
                          }
                      }
                  }
              })
              return ret
          }

          10.Promise的實(shí)現(xiàn)

          實(shí)現(xiàn) Promise 需要完全讀懂 Promise A+ 規(guī)范,不過從總體的實(shí)現(xiàn)上看,有如下幾個(gè)點(diǎn)需要考慮到:

          • Promise本質(zhì)是一個(gè)狀態(tài)機(jī),且狀態(tài)只能為以下三種:Pending(等待態(tài))、Fulfilled(執(zhí)行態(tài))、Rejected(拒絕態(tài)),狀態(tài)的變更是單向的,只能從Pending -> Fulfilled 或 Pending -> Rejected,狀態(tài)變更不可逆
          • then 需要支持鏈?zhǔn)秸{(diào)用
          class Promise {
              callbacks = [];
              state = 'pending';//增加狀態(tài)
              value = null;//保存結(jié)果
              constructor(fn) {
                  fn(this._resolve.bind(this), this._reject.bind(this));
              }
              then(onFulfilled, onRejected) {
                  return new Promise((resolve, reject) => {
                      this._handle({
                          onFulfilled: onFulfilled || null,
                          onRejected: onRejected || null,
                          resolve: resolve,
                          reject: reject
                      });
                  });
              }
              _handle(callback) {
                  if (this.state === 'pending') {
                      this.callbacks.push(callback);
                      return;
                  }
           
                  let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
           
                  if (!cb) {//如果then中沒有傳遞任何東西
                      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
                      cb(this.value);
                      return;
                  }
           
                  let ret = cb(this.value);
                  cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
                  cb(ret);
              }
              _resolve(value) {
           
                  if (value && (typeof value === 'object' || typeof value === 'function')) {
                      var then = value.then;
                      if (typeof then === 'function') {
                          then.call(value, this._resolve.bind(this), this._reject.bind(this));
                          return;
                      }
                  }
           
                  this.state = 'fulfilled';//改變狀態(tài)
                  this.value = value;//保存結(jié)果
                  this.callbacks.forEach(callback => this._handle(callback));
              }
              _reject(error) {
                  this.state = 'rejected';
                  this.value = error;
                  this.callbacks.forEach(callback => this._handle(callback));
              }
          }

          Promise.resolve

          • Promsie.resolve(value) 可以將任何值轉(zhuǎn)成值為 value 狀態(tài)是 fulfilled 的 Promise,但如果傳入的值本身是 Promise 則會原樣返回它。
          Promise.resolve(value) {
            if (value && value instanceof Promise) {
              return value;
            } else if (value && typeof value === 'object' && typeof value.then === 'function') {
              let then = value.then;
              return new Promise(resolve => {
                then(resolve);
              });
            } else if (value) {
              return new Promise(resolve => resolve(value));
            } else {
              return new Promise(resolve => resolve());
            }
          }

          Promise.reject

          • 和 Promise.resolve() 類似,Promise.reject() 會實(shí)例化一個(gè) rejected 狀態(tài)的 Promise。但與 Promise.resolve() 不同的是,如果給 Promise.reject() 傳遞一個(gè) Promise 對象,則這個(gè)對象會成為新 Promise 的值。
          Promise.reject = function(reason) {
              return new Promise((resolve, reject) => reject(reason))
          }

          Promise.all

          • 傳入的所有 Promsie 都是 fulfilled,則返回由他們的值組成的,狀態(tài)為 fulfilled 的新 Promise;
          • 只要有一個(gè) Promise 是 rejected,則返回 rejected 狀態(tài)的新 Promsie,且它的值是第一個(gè) rejected 的 Promise 的值;
          • 只要有一個(gè) Promise 是 pending,則返回一個(gè) pending 狀態(tài)的新 Promise;
          Promise.all = function(promiseArr) {
              let index = 0, result = []
              return new Promise((resolve, reject) => {
                  promiseArr.forEach((p, i) => {
                      Promise.resolve(p).then(val => {
                          index++
                          result[i] = val
                          if (index === promiseArr.length) 
          {
                              resolve(result)
                          }
                      }, err => {
                          reject(err)
                      })
                  })
              })
          }

          Promise.race

          • Promise.race 會返回一個(gè)由所有可迭代實(shí)例中第一個(gè) fulfilled 或 rejected 的實(shí)例包裝后的新實(shí)例。
          Promise.race = function(promiseArr) {
              return new Promise((resolve, reject) => {
                  promiseArr.forEach(p => {
                      Promise.resolve(p).then(val => {
                          resolve(val)
                      }, err => {
                          rejecte(err)
                      })
                  })
              })
          }

          11.Ajax的實(shí)現(xiàn)

          function ajax(url,method,body,headers){
              return new Promise((resolve,reject)=>{
                  let req = new XMLHttpRequest();
                  req.open(methods,url);
                  for(let key in headers){
                      req.setRequestHeader(key,headers[key])
                  }
                  req.onreadystatechange(()=>{
                      if(req.readystate == 4){
                          if(req.status >= '200' && req.status <= 300){
                              resolve(req.responeText)
                          }else{
                              reject(req)
                          }
                      }
                  })
                  req.send(body)
              })
          }

          12.實(shí)現(xiàn)防抖函數(shù)(debounce)

          • 連續(xù)觸發(fā)在最后一次執(zhí)行方法,場景:輸入框匹配
          let debounce = (fn,time = 1000) => {
              let timeLock = null

              return function (...args)
          {
                  clearTimeout(timeLock)
                  timeLock = setTimeout(()=>{
                      fn(...args)
                  },time)
              }
          }

          13.實(shí)現(xiàn)節(jié)流函數(shù)(throttle)

          • 在一定時(shí)間內(nèi)只觸發(fā)一次,場景:長列表滾動節(jié)流
          let throttle = (fn,time = 1000) => {
              let flag = true;

              return function (...args){
                  if(flag){
                      flag = false;
                      setTimeout(()=>{
                          flag = true;
                          fn(...args)
                      },time)
                  }
              }
          }

          14.深拷貝(deepclone)

          • 判斷類型,正則和日期直接返回新對象
          • 空或者非對象類型,直接返回原值
          • 考慮循環(huán)引用,判斷如果hash中含有直接返回hash中的值
          • 新建一個(gè)相應(yīng)的new obj.constructor加入hash
          • 遍歷對象遞歸(普通key和key是symbol情況)
          function deepClone(obj,hash = new WeakMap()){
              if(obj instanceof RegExp) return new RegExp(obj);
              if(obj instanceof Date) return new Date(obj);
              if(obj === null || typeof obj !== 'object'return obj;
              //循環(huán)引用的情況
              if(hash.has(obj)){
                  return hash.get(obj)
              }
              //new 一個(gè)相應(yīng)的對象
              //obj為Array,相當(dāng)于new Array()
              //obj為Object,相當(dāng)于new Object()
              let constr = new obj.constructor();
              hash.set(obj,constr);
              for(let key in obj){
                  if(obj.hasOwnProperty(key)){
                      constr[key] = deepClone(obj[key],hash)
                  }
              }
              //考慮symbol的情況
              let symbolObj = Object.getOwnPropertySymbols(obj)
              for(let i=0;i<symbolObj.length;i++){
                  if(obj.hasOwnProperty(symbolObj[i])){
                      constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)
                  }
              }
              return constr
          }

          15.數(shù)組扁平化的實(shí)現(xiàn)(flat)

          let arr = [1,2,[3,4,[5,[6]]]]
          console.log(arr.flat(Infinity))//flat參數(shù)為指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1
          //用reduce實(shí)現(xiàn)
          function fn(arr){
             return arr.reduce((prev,cur)=>{
                return prev.concat(Array.isArray(cur)?fn(cur):cur)
             },[])
          }

          16.函數(shù)柯里化

          function sumFn(a,b,c){return a+ b + c};
          let sum = curry(sumFn);
          sum(2)(3)(5)//10
          sum(2,3)(5)//10
          function curry(fn,...args){
            let fnLen = fn.length,
                argsLen = args.length;
            //對比函數(shù)的參數(shù)和當(dāng)前傳入?yún)?shù)
            //若參數(shù)不夠就繼續(xù)遞歸返回curry
            //若參數(shù)夠就調(diào)用函數(shù)返回相應(yīng)的值
            if(fnLen > argsLen){
              return function(...arg2s){
                return curry(fn,...args,...arg2s)
              }
            }else{
              return fn(...args)
            }
          }

          17.使用閉包實(shí)現(xiàn)每隔一秒打印 1,2,3,4

          for (var i=1; i<=5; i++) {
            (function (i) {
              setTimeout(() => console.log(i), 1000*i)
            })(i)
          }

          18.手寫一個(gè) jsonp

          const jsonp = function (url, data) {
              return new Promise((resolve, reject) => {
                  // 初始化url
                  let dataString = url.indexOf('?') === -1 ? '?' : ''
                  let callbackName = `jsonpCB_${Date.now()}`
                  url += `${dataString}callback=${callbackName}`
                  if (data) {
                      // 有請求參數(shù),依次添加到url
                      for (let k in data) {
                          url += `${k}=${data[k]}`
                      }
                  }
                  let jsNode = document.createElement('script')
                  jsNode.src = url
                  // 觸發(fā)callback,觸發(fā)后刪除js標(biāo)簽和綁定在window上的callback
                  window[callbackName] = result => {
                      delete window[callbackName]
                      document.body.removeChild(jsNode)
                      if (result) {
                          resolve(result)
                      } else {
                          reject('沒有返回?cái)?shù)據(jù)')
                      }
                  }
                  // js加載異常的情況
                  jsNode.addEventListener('error', () => {
                      delete window[callbackName]
                      document.body.removeChild(jsNode)
                      reject('JavaScript資源加載失敗')
                  }, false)
                  // 添加js節(jié)點(diǎn)到document上時(shí),開始請求
                  document.body.appendChild(jsNode)
              })
          }
          jsonp('http://192.168.0.103:8081/jsonp', {
              a: 1,
              b: 'heiheihei'
          })
          .then(result => {
              console.log(result)
          })
          .catch(err => {
              console.error(err)
          })

          19.手寫一個(gè)觀察者模式

          class Subject{
            constructor(name){
              this.name = name
              this.observers = []
              this.state = 'XXXX'
            }
            // 被觀察者要提供一個(gè)接受觀察者的方法
            attach(observer){
              this.observers.push(observer)
            }

            // 改變被觀察著的狀態(tài)
            setState(newState){
              this.state = newState
              this.observers.forEach(o=>{
                o.update(newState)
              })
            }
          }

          class Observer{
            constructor(name){
              this.name = name
            }

            update(newState){
              console.log(`${this.name}say:${newState}`)
            }
          }

          // 被觀察者 燈
          let sub = new Subject('燈')
          let mm = new Observer('小明')
          let jj = new Observer('小健')
           
          // 訂閱 觀察者
          sub.attach(mm)
          sub.attach(jj)
           
          sub.setState('燈亮了來電了')

          20.EventEmitter 實(shí)現(xiàn)

          class EventEmitter {
              constructor() {
                  this.events = {};
              }
              on(event, callback) {
                  let callbacks = this.events[event] || [];
                  callbacks.push(callback);
                  this.events[event] = callbacks;
                  return this;
              }
              off(event, callback) {
                  let callbacks = this.events[event];
                  this.events[event] = callbacks && callbacks.filter(fn => fn !== callback);
                  return this;
              }
              emit(event, ...args) {
                  let callbacks = this.events[event];
                  callbacks.forEach(fn => {
                      fn(...args);
                  });
                  return this;
              }
              once(event, callback) {
                  let wrapFun = function (...args) {
                      callback(...args);
                      this.off(event, wrapFun);
                  };
                  this.on(event, wrapFun);
                  return this;
              }
          }

          21.生成隨機(jī)數(shù)的各種方法?

          function getRandom(min, max) {
            return Math.floor(Math.random() * (max - min)) + min   
          }

          22.如何實(shí)現(xiàn)數(shù)組的隨機(jī)排序?

          let arr = [2,3,454,34,324,32]
          arr.sort(randomSort)
          function randomSort(a, b) {
            return Math.random() > 0.5 ? -1 : 1;
          }

          23.寫一個(gè)通用的事件偵聽器函數(shù)。

          const EventUtils = {
            // 視能力分別使用dom0||dom2||IE方式 來綁定事件
            // 添加事件
            addEvent: function(element, type, handler) {
              if (element.addEventListener) {
                element.addEventListener(type, handler, false);
              } else if (element.attachEvent) {
                element.attachEvent("on" + type, handler);
              } else {
                element["on" + type] = handler;
              }
            },
            // 移除事件
            removeEvent: function(element, type, handler) {
              if (element.removeEventListener) {
                element.removeEventListener(type, handler, false);
              } else if (element.detachEvent) {
                element.detachEvent("on" + type, handler);
              } else {
                element["on" + type] = null;
              }
            },
           // 獲取事件目標(biāo)
            getTarget: function(event) {
              return event.target || event.srcElement;
            },
            // 獲取 event 對象的引用,取到事件的所有信息,確保隨時(shí)能使用 event
            getEvent: function(event) {
              return event || window.event;
            },
           // 阻止事件(主要是事件冒泡,因?yàn)?nbsp;IE 不支持事件捕獲)
            stopPropagation: function(event) {
              if (event.stopPropagation) {
                event.stopPropagation();
              } else {
                event.cancelBubble = true;
              }
            },
            // 取消事件的默認(rèn)行為
            preventDefault: function(event) {
              if (event.preventDefault) {
                event.preventDefault();
              } else {
                event.returnValue = false;
              }
            }
          };

          24.使用迭代的方式實(shí)現(xiàn) flatten 函數(shù)。

          var arr = [123, [45], [6, [7, [8]]]]
          /** * 使用遞歸的方式處理 * wrap 內(nèi)保
          存結(jié)果 ret * 返回一個(gè)遞歸函數(shù) **/

          function wrap() {
              var ret = [];
              return function flat(a) {
                  for (var item of
                      a) {
                          if (item.constructor === Array) {
                              ret.concat(flat(item))
                          } else {
                              ret.push(item)
                          }
                  }
                  return ret
              }

          console.log(wrap()(arr));

          25.怎么實(shí)現(xiàn)一個(gè)sleep

          • sleep函數(shù)作用是讓線程休眠,等到指定時(shí)間在重新喚起。
          function sleep(delay) {
            var start = (new Date()).getTime();
            while ((new Date()).getTime() - start < delay) {
              continue;
            }
          }

          function test() {
            console.log('111');
            sleep(2000);
            console.log('222');
          }

          test()

          26.實(shí)現(xiàn)正則切分千分位(10000 => 10,000)

          //無小數(shù)點(diǎn)
          let num1 = '1321434322222'
          num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,')
          //有小數(shù)點(diǎn)
          let num2 = '342243242322.3432423'
          num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,')

          27.對象數(shù)組去重

          輸入:
          [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
          輸出:
          [{a:1,b:2,c:3},{d:2,c:2}]
          • 首先寫一個(gè)函數(shù)把對象中的key排序,然后再轉(zhuǎn)成字符串
          • 遍歷數(shù)組利用Set將轉(zhuǎn)為字符串后的對象去重
          function objSort(obj){
              let newObj = {}
              //遍歷對象,并將key進(jìn)行排序
              Object.keys(obj).sort().map(key => {
                  newObj[key] = obj[key]
              })
              //將排序好的數(shù)組轉(zhuǎn)成字符串
              return JSON.stringify(newObj)
          }

          function unique(arr){
              let set = new Set();
              for(let i=0;i<arr.length;i++){
                  let str = objSort(arr[i])
                  set.add(str)
              }
              //將數(shù)組中的字符串轉(zhuǎn)回對象
              arr = [...set].map(item => {
                  return JSON.parse(item)
              })
              return arr
          }

          28.解析 URL Params 為對象

          let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
          parseParam(url)
          /* 結(jié)果
          { user: 'anonymous',
            id: [ 123, 456 ], // 重復(fù)出現(xiàn)的 key 要組裝成數(shù)組,能被轉(zhuǎn)成數(shù)字的就轉(zhuǎn)成數(shù)字類型
            city: '北京', // 中文需解碼
            enabled: true, // 未指定值得 key 約定為 true
          }
          */

          function parseParam(url) {
            const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 將 ? 后面的字符串取出來
            const paramsArr = paramsStr.split('&'); // 將字符串以 & 分割后存到數(shù)組中
            let paramsObj = {};
            // 將 params 存到對象中
            paramsArr.forEach(param => {
              if (/=/.test(param)) { // 處理有 value 的參數(shù)
                let [key, val] = param.split('='); // 分割 key 和 value
                val = decodeURIComponent(val); // 解碼
                val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判斷是否轉(zhuǎn)為數(shù)字

                if (paramsObj.hasOwnProperty(key)) { // 如果對象有 key,則添加一個(gè)值
                  paramsObj[key] = [].concat(paramsObj[key], val);
                } else { // 如果對象沒有這個(gè) key,創(chuàng)建 key 并設(shè)置值
                  paramsObj[key] = val;
                }
              } else { // 處理沒有 value 的參數(shù)
                paramsObj[param] = true;
              }
            })

            return paramsObj;
          }

          29.模板引擎實(shí)現(xiàn)

          let template = '我是{{name}},年齡{{age}},性別{{sex}}';
          let data = {
            name: '姓名',
            age: 18
          }
          render(template, data); // 我是姓名,年齡18,性別undefined
          function render(template, data) {
            const reg = /\{\{(\w+)\}\}/; // 模板字符串正則
            if (reg.test(template)) { // 判斷模板里是否有模板字符串
              const name = reg.exec(template)[1]; // 查找當(dāng)前模板里第一個(gè)模板字符串的字段
              template = template.replace(reg, data[name]); // 將第一個(gè)模板字符串渲染
              return render(template, data); // 遞歸的渲染并返回渲染后的結(jié)構(gòu)
            }
            return template; // 如果模板沒有模板字符串直接返回
          }

          30.轉(zhuǎn)化為駝峰命名

          var s1 = "get-element-by-id"
          // 轉(zhuǎn)化為 getElementById
          var f = function(s) {
              return s.replace(/-\w/g, function(x) {
                  return x.slice(1).toUpperCase();
              })
          }

          31.查找字符串中出現(xiàn)最多的字符和個(gè)數(shù)

          • 例: abbcccddddd -> 字符最多的是d,出現(xiàn)了5次
          let str = "abcabcabcbbccccc";
          let num = 0;
          let char = '';

           // 使其按照一定的次序排列
          str = str.split('').sort().join('');
          // "aaabbbbbcccccccc"

          // 定義正則表達(dá)式
          let re = /(\w)\1+/g;
          str.replace(re,($0,$1) => {
              if(num < $0.length){
                  num = $0.length;
                  char = $1;        
              }
          });
          console.log(`字符最多的是${char},出現(xiàn)了${num}次`);

          32.圖片懶加載

          let imgList = [...document.querySelectorAll('img')]
          let length = imgList.length

          const imgLazyLoad = function() {
              let count = 0
              return (function() {
                  let deleteIndexList = []
                  imgList.forEach((img, index) => {
                      let rect = img.getBoundingClientRect()
                      if (rect.top < window.innerHeight) {
                          img.src = img.dataset.src
                          deleteIndexList.push(index)
                          count++
                          if (count === length) {
                              document.removeEventListener('scroll', imgLazyLoad)
                          }
                      }
                  })
                  imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
              })()
          }

          // 這里最好加上防抖處理
          document.addEventListener('scroll', imgLazyLoad)

          參考資料


          作者:xpsilvester

          原文鏈接:https://juejin.cn/post/6963167124881670152

          如果覺得這篇文章還不錯
          點(diǎn)擊下面卡片關(guān)注我
          來個(gè)【分享、點(diǎn)贊、在看】三連支持一下吧

             “分享、點(diǎn)贊在看” 支持一波 

          瀏覽 38
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  香蕉大香蕉久 | 男男久久久 | 91av视频在线 | 国产白丝袜足交网站大全 | 国产精品xxx |