前端常見手寫代碼

1.call()函數(shù)的實現(xiàn)步驟

  • 判斷調(diào)用對象是否為函數(shù),即使是定義在函數(shù)的原型上的,但是可能出現(xiàn)使用 call 等方式調(diào)用的情況。
  • 判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為 window 。
  • 處理傳入的參數(shù),截取第一個參數(shù)后的所有參數(shù)。
  • 將函數(shù)作為上下文對象的一個屬性。
  • 使用上下文對象來調(diào)用這個方法,并保存返回結(jié)果。
  • 刪除剛才新增的屬性。
  • 返回結(jié)果。

代碼實現(xiàn):

let A = {
      name: 'zs',
      getName: function(msg){
        return msg + this.name
      }
    }
    let B = {
      name: 'lis'
    }
    Function.prototype.myCall = function(context) {
      //判斷調(diào)用對象是否為函數(shù)
      if(typeof this !== 'function') {
        console.error('type err');
        return;
      }
      //判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為window
      context = context || window;
      //處理傳入?yún)?shù),截取第一個參數(shù)后的所有參數(shù)
      let args = [...arguments].slice(1);
      //將函數(shù)作為上下文對象的一個屬性
      context.fn = this;
      //使用上下文對象調(diào)用這個方法,并保存返回結(jié)果。
      const res = context.fn(...args);
      //刪除剛剛新增的屬性
      delete context.fn;
      //返回結(jié)果
      return res
    }
    console.log('封裝',A.getName.myCall(B,'信息'));//封裝 信息lis
    console.log('原生',A.getName.call(B,'hello')); //原生 hello lis

2. apply()函數(shù)的實現(xiàn)步驟

  • 判斷調(diào)用對象是否為函數(shù),即使是定義在函數(shù)的原型上的,但是可能出現(xiàn)使用 call 等方式調(diào)用的情況。
  • 判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為 window 。
  • 將函數(shù)作為上下文對象的一個屬性。
  • 判斷參數(shù)值是否傳入
  • 使用上下文對象來調(diào)用這個方法,并保存返回結(jié)果。
  • 刪除剛才新增的屬性
  • 返回結(jié)果

代碼實現(xiàn):

let A = {
      name: 'zs',
      getName: function(msg){
        return msg + this.name
      }
    }
    let B = {
      name: 'lis'
    }
    Function.prototype.myApply = function(context) {
      //判斷調(diào)用對象是否為函數(shù)
      if(typeof this !== 'function') {
        console.error('type err');
        return;
      }
      //判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為window
      context = context || window;
      
      //將函數(shù)作為上下文對象的一個屬性
      context.fn = this;
      let res = null;
      //判斷參數(shù)是否傳入
      console.log(arguments)
      if(arguments[1]) {
        res = context.fn(...arguments[1]);
      } else {
        res = context.fn();
      }    
      //刪除剛剛新增的屬性
      delete context.fn;
      //返回結(jié)果
      return res
    }
    console.log('封裝',A.getName.myApply(B,['信息']));//封裝 信息lis
    console.log('原生',A.getName.apply(B,['原生'])); //原生 原生 lis

3.bind()函數(shù)的實現(xiàn)步驟

  • 接收的參數(shù):第一個參數(shù)為this的指向,后面的參數(shù)就是函數(shù)接收的參數(shù)了。this是必需提供的參數(shù),其他的參數(shù)都是非必需的;
  • 返回值:返回值應(yīng)該是一個函數(shù);

代碼實現(xiàn)

let A = {
      name: 'zs',
      getName: function(msg){
        return msg + this.name
      }
    }
    let B = {
      name: 'lis'
    }
    //封裝bind函數(shù)
    Function.prototype.myBind = function(context,...args) {
      return ()=> {
        return this.apply(context,...args)
      };
    }
    let res = A.getName.myBind(B,['封裝']);
    console.log(res());//封裝lis
    let result = A.getName.bind(B,['原生']);
    console.log(result());//原生lis

4.防抖節(jié)流實現(xiàn)和應(yīng)用場景

1.防抖實現(xiàn)

觸發(fā)高頻事件后,n秒內(nèi)函數(shù)之后執(zhí)行一次,如果n秒內(nèi)再次觸發(fā),會重新計算事件。

  • 應(yīng)用場景(一般用于點擊發(fā)送請求)

應(yīng)用場景:一般是onrize,onscroll等這些頻繁觸發(fā)的函數(shù),比如你想獲取滾動條的位置,然后執(zhí)行下一步動作;鼠標不斷點擊觸發(fā),mousedown(單位時間內(nèi)只觸發(fā)一次),input輸入....

  • 代碼實現(xiàn)
function debounce(fn, wait) {
      let timer=null;
      let that = this;
      let args = arguments;
      return function () {
      //每次點擊清除當前定時器
        clearTimeout(timer);
        timer = setTimeout(() => {
          timer = null;
          //將傳入函數(shù)this指向當前函數(shù)。
          fn.apply(that,args);
        }, wait)
      }
    }
    
    <input type="text" placeholder="請輸入">
    let input = document.querySelector('input');
    input.addEventListener('input',debounce(()=>{
      console.log('input');
    },1000))
   // 輸入完成之后一秒后才會執(zhí)行回調(diào)

2.節(jié)流實現(xiàn)

觸發(fā)高頻事件后,n秒內(nèi)函數(shù)函數(shù)只會執(zhí)行一次。

  • 1.應(yīng)用場景

應(yīng)用場景: search搜索聯(lián)想,用戶在不斷輸入值時

  • 代碼實現(xiàn)
function throttle(fn, wait) {
      let timer=null;
      let that = this;
      let args = arguments;
      return function () {
      //wait秒之內(nèi)用戶點擊只會執(zhí)行一次定時器
        if (timer) return
        timer = setTimeout(() => {
          timer = null;
          clearTimeout(timer);
          fn.apply(that,args);
        }, wait)
      }
    }
    
    <button>點擊</button>
    let button = document.querySelector('button')
     button.addEventListener('click',throttle(()=>{
      console.log('button');
    },1000))
    //一秒之內(nèi)只會觸發(fā)一次回調(diào)函數(shù)

5.淺拷貝和深拷貝實的現(xiàn)步驟

5.1淺拷貝的實現(xiàn)

只拷貝對象或數(shù)組第一層

更深層次對象級別的只拷貝引用(只將地址拷貝給了新對象)

注意:第一層不是對象時,淺拷貝也是深拷貝,修改值時不會影響新對象的值變化

代碼實現(xiàn):

const target = {
      a: '1',
      b: {c:'1'}
    }

    //只拷貝對象或數(shù)組第一層
    function shallClone(target) {
      if (typeof target === 'object' && target !== null) {
        let cloneTarget = Array.isArray(target)?[]:{};
        for(let p in target) {
          cloneTarget[p] = target[p];
        }
        return cloneTarget;
      } else {
        return target
      }
    }
    let cloneTarget = shallClone(target);

5.2深拷貝實現(xiàn)(舉兩個例子)

深拷貝(復(fù)雜):復(fù)制變量值,對于非基本類型的變量,則遞歸至基本類型變量后,再復(fù)制。

基本數(shù)據(jù)類型(undefined,null,number,String,boolean,bigInt):存放在棧中,

引用數(shù)據(jù)類型(Object,Array,Function): 存放在堆中。

  • 1.第一種實現(xiàn)方法

    JSON.parse(JSON.stringify())

function deepCopy(obj) {
    return JSON.parse(JSON.stringify(obj))
}

var obj = {
    age: 1,
    name: undefined,
    sex: null,
    tel: /^1[34578]\d{9}$/,
    say: () => {
        console.log('hahha')
    }
}
console.log(deepCopy(obj)); // { age: 1, sex: null, tel: {} }

注意:這種拷貝方法不可以拷貝一些特殊的屬性(例如正則表達式,undefine,function)

  • 第二種實現(xiàn)方法 用遞歸去復(fù)制所有層級屬性
function deepCopyTwo(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj == 'object') {
        for (const key in obj) {
            //判斷obj子元素是否為對象,如果是,遞歸復(fù)制
            if (obj[key] && typeof obj[key] === "object") {
                objClone[key] = deepCopyTwo(obj[key]);
            } else {
                //如果不是,簡單復(fù)制
                objClone[key] = obj[key];
            }
        }
    }
    return objClone;
}
var obj = {
    age: 1,
    name: undefined,
    sex: null,
    tel: /^1[34578]\d{9}$/,
    say: () => {
        console.log('hahha')
    }
}
console.log(deepCopy(obj));  // { age: 1, sex: null, tel: {} }

6.new 操作實現(xiàn)的步驟

new 操作過程具體做了什么

  • 創(chuàng)建一個新的對象obj
  • 將對象與構(gòu)建函數(shù)通過原型鏈連接起來
  • 將構(gòu)建函數(shù)中的this綁定到新建的對象obj
  • 根據(jù)構(gòu)建函數(shù)返回類型作判斷,如果是原始值則被忽略,如果是返回對象,需要正常處理。
function Person (name,age){
      this.name= name;
      this.age = age
    }

    function myNew(Fn,...args) {
      let child = Object.create(Fn.prototype);
      //相當于
      // child.__proto__ == Fn.prototype

      //將傳入對象的this指向?qū)ο蟮脑玩溕?      let result = Fn.apply(child,args);
      return (typeof result === 'object' || typeof result === 'function') ? result : child;
    }
    let child = myNew(Person,'zs','22');
    console.log(child.name,child.age); //zs,22

7.instanceof 的實現(xiàn)步驟

instanceof 運算符用于檢測構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在某個實例對象的原型鏈上。

        //封裝instanceof
    //L: 實例對象 R: Function 原型鏈
    function instance_of(L,R) {
      R = R.prototype;
      L = L.__proto__;
      while(true) {
        if(L === null) return false;
        if(L === R) return true;
        L = L.__proto__;
      }
    }

8.Object.create() 底層實現(xiàn)

Object.create()方法創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的proto。

function myCreate(father){
    const obj = {};
    obj.__proto__ = father;
    return obj;
}
//使用
let Person = {}
let child = myCreate(Person.prototype);
let child = Object.create(Person.prototype);
//相當于
child.__proto = Person.prototype;

9.原生js 封裝ajax 實現(xiàn)的步驟

  • 原生Ajax五個基本步驟

1、創(chuàng)建ajax對象
var xhr = new XMLHttpRequest();

2、用ajax對象的open方法設(shè)置連接服務(wù)器的參數(shù)
xhr.open( method,url,async);
method:請求類型,post或get
url:請求文件的具體地址
async::是否異步(true為異步,false為同步)

3、設(shè)置發(fā)送數(shù)據(jù)的頭部,一般用來說明數(shù)據(jù)格式,如:
xhr.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
注: multipart/form-data(一般在發(fā)送文件時使用,以二進制形式發(fā)送)和application/json(發(fā)送json格式數(shù)據(jù))

4、發(fā)送請求,xhr.send(數(shù)據(jù))

5、判斷通訊狀態(tài)或接收返回數(shù)據(jù),這個是寫在第三個步驟回調(diào)函數(shù)里邊的

function ajax(url) {
      let res =null;
      //1.創(chuàng)建異步請求對象
      let xhr = new XMLHttpRequest();
      // 2.配置 ajax 請求地址
      xhr.open('get',url,true);
      // 3.設(shè)置響應(yīng)頭
      xhr.setRequestHeader(
        "Content-Type",
        "application/x-www-form-urlencoded"
      );
      // 4.綁定監(jiān)聽事件
      xhr.onreadystatechange=()=>{
        // 當異步請求狀態(tài)為4時,并且返回的狀態(tài)碼為200,接收響應(yīng)成功。
        if(xhr.readyState =='4' && xhr.status == '200') {
          res = JSON.parse(xhr.responseText)
        }
        res.xhr.responseText;
      }
      // 5.發(fā)送請求
      xhr.send(null);
    }

10.使用promise封裝ajax請求

function ajax(url) {
      return new Promise((resolve, reject) => {
        //1.創(chuàng)建異步請求對象
        let xhr = new XMLHttpRequest();
        // 2.配置 ajax 請求地址
        xhr.open('get', url, true);
        // 3.設(shè)置響應(yīng)頭
        xhr.setRequestHeader(
          "Content-Type",
          "application/x-www-form-urlencoded"
        );
        // 4.綁定監(jiān)聽事件
        //每當readyState改變時,都會調(diào)用這個函數(shù)。
        xhr.onreadystatechange = () => {
          // 當異步請求狀態(tài)為4時,請求已完成,并且準備就緒
          if (xhr.readyState == '4') {
            //如果200,代表請求成功
            if (xhr.status == '200')
              resolve(xhr.responseText)
          }else if(xhr.status == '404') {
            reject(new Error('404 NOT FOUND'))
          }
          res.xhr.responseText;
        }
        // 5.發(fā)送請求
        xhr.send(null);
      })
    }

    //調(diào)用
    ajax('/api/user')
    .then(res=>{ })
    .catch(err=>console.log(err))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容